Android Open Source - MorseFlash Main Activity






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 ww  .  ja v  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.ui;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.view.WindowManager;

import com.ovrhere.android.morseflash.R;
import com.ovrhere.android.morseflash.morsecode.dictionaries.InternationalMorseCode;
import com.ovrhere.android.morseflash.morsecode.transcriber.MorseTranscriber;
import com.ovrhere.android.morseflash.morsecode.transcriber.MorseTranscriberHeadlessFragment;
import com.ovrhere.android.morseflash.prefs.PreferenceUtils;
import com.ovrhere.android.morseflash.ui.fragments.MainFragment;
import com.ovrhere.android.morseflash.ui.fragments.ScreenFlashFragment;
import com.ovrhere.android.morseflash.utils.CameraFlashUtil;

/**
 * The main activity for the application. This is the primary entry point
 * of the app.
 * @author Jason J.
 * @version 0.5.4-20140719
 */
public class MainActivity extends ActionBarActivity implements
  MainFragment.OnFragmentInteractionListener,
  ScreenFlashFragment.OnFragmentInteraction,
  MorseTranscriber.OnSignalListener,
  MorseTranscriber.OnTranscriptionListener {
  /** The tag for logs. */
  final static private String CLASS_NAME = MainActivity.class.getSimpleName();
  /** The tag for morse transcriber headless fragment. */
  final static private String MORSE_TRANSCRIBER_TAG = 
      MorseTranscriberHeadlessFragment.class.getName();
  
  /** Bundle key: The current fragment tag. String. */
  final static private String KEY_CURRENT_FRAG_TAG = 
      CLASS_NAME + ".KEY_CURRENT_FRAG_TAG";
  /** Bundle key: Whether the screen is fullscreen. Boolean. */
  final static private String KEY_IS_FULLSCREEN = 
      CLASS_NAME + ".KEY_IS_FULLSCREEN";    
  /** Bundle key: The message for the input screen. String. */
  final static private String KEY_INPUT_MESSAGE = 
      CLASS_NAME + ".KEY_INPUT_MESSAGE";
  
  /** Bundle key: The bool for if the message is currently being sent. boolean. */
  final static private String KEY_IS_SENDING_MESSAGE = 
      CLASS_NAME + ".KEY_IS_SENDING_MESSAGE";
    
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// end constants
  ////////////////////////////////////////////////////////////////////////////////////////////////
  /** The morse transcriber as set in the headless fragment. */
  private MorseTranscriber morseTranscriber  = null;
  
  /** The reference to the flasher, if attached. */
  private ScreenFlashFragment flashFrag = null;
  /** The reference to the main fragment, if attached. */
  private MainFragment mainFrag = null;
  /** The reference to the camera flash util to flash light. 
   * Should be destroyed in onPause. */
  private CameraFlashUtil maincameraFlashUtil = null;
  
  /** The current fragment tag. Top level is {@link MainFragment#TAG}. 
   * Default empty string.*/
  private String currentFragmentTag = "";
  /** Whether the screen is currently fullscreen. */
  private boolean isFullscreen = false;
  /** The input message for the main fragment. */
  private String inputMessage = "";
  /** If the message is being sent by flash light. */
  private boolean isMessageByFlashLight = false;
  /** If the message is currently being sent. Set <code>true</code> in
   * {@link #onSendButton(String)} and <code>false</code> in 
   * {@link #onCancelButton()}.   */
  private boolean isSending = false;
  
  /** Activity visibility. Set on #onResume() and #onPause(); */
  private boolean activityVisible = false;
  /** The count of unseen signals during {@link #activityVisible} == false
   * Resets to 0 in {@link #onResume()}   */
  private int unseenSignals = 0;
  
  /** The reference to shared preferences for the application. */
  private SharedPreferences prefs = null;
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// End members
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(KEY_CURRENT_FRAG_TAG, currentFragmentTag);
    outState.putBoolean(KEY_IS_FULLSCREEN, isFullscreen);
    outState.putString(KEY_INPUT_MESSAGE, inputMessage);
    outState.putBoolean(KEY_IS_SENDING_MESSAGE, isSending);
  }
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    prefs = PreferenceUtils.getPreferences(this);
    
    setContentView(R.layout.activity_main);
    FragmentManager manager = getSupportFragmentManager();
    
    MorseTranscriberHeadlessFragment mtFrag = (MorseTranscriberHeadlessFragment)
        manager.findFragmentByTag(MORSE_TRANSCRIBER_TAG);
    if (mtFrag == null ){
      mtFrag = new MorseTranscriberHeadlessFragment();
      morseTranscriber = new MorseTranscriber(new InternationalMorseCode(), this);
      manager.beginTransaction()
          .add(mtFrag, MORSE_TRANSCRIBER_TAG)
          .commit();
      mtFrag.setMorseTranscriber(morseTranscriber);
    } else {
      morseTranscriber = mtFrag.getMorseTranscriber();
      if (morseTranscriber == null){
        morseTranscriber = new MorseTranscriber(new InternationalMorseCode(), this);
        mtFrag.setMorseTranscriber(morseTranscriber);
      } else {
        morseTranscriber.setOnSignalListener(this);
      }
    }
    morseTranscriber.setOnMorseListener(this);
    
    if (savedInstanceState == null) {
      setFragToDefault();
    } else {
      currentFragmentTag = savedInstanceState.getString(KEY_CURRENT_FRAG_TAG);
      //if stored state is fullscreen, maintain it.
      if (savedInstanceState.getBoolean(KEY_IS_FULLSCREEN)){
        setFullscreen(true);
      }
      if (savedInstanceState.getString(KEY_INPUT_MESSAGE) != null){
        inputMessage = savedInstanceState.getString(KEY_INPUT_MESSAGE);
      }
      isSending =  savedInstanceState.getBoolean(KEY_IS_SENDING_MESSAGE);
    }
  }  
    
  
  @Override
  protected void onDestroy() {
    super.onDestroy();
    if (morseTranscriber != null){
      //clean up activity references
      morseTranscriber.setOnSignalListener(null);
      morseTranscriber.cancel();
    }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
      return true;
    }
    return super.onOptionsItemSelected(item);
  }
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// End creation
  ////////////////////////////////////////////////////////////////////////////////////////////////

  @Override
  protected void onResume() {
    super.onResume();
    activityVisible = true;
    if (unseenSignals > 0){
      //if there were unseen signals, we must have suspended.
      unseenSignals = 0;
      onCancelButton(); //cancel signal.
    }
  };
  
  @Override
  protected void onPause() {
    super.onPause();
    activityVisible = false;
  }
  
  
  @Override
  public void onAttachFragment(Fragment fragment) {
    super.onAttachFragment(fragment);
    if (fragment instanceof ScreenFlashFragment){
      flashFrag = (ScreenFlashFragment) fragment;
      flashFrag.flashBackground(false);
    } else if (fragment instanceof MainFragment){
      mainFrag = ((MainFragment) fragment);      
    }
  }
  
  
  @Override
  public void onBackPressed() {
    if (currentFragmentTag.equals(MainFragment.TAG)){
      super.onBackPressed();
    } else if (currentFragmentTag.equals(ScreenFlashFragment.class.getName())){
      onCancelButton();
    } else {
      //we currently have one depth, if we had more we'd keep a trail
      setFragToDefault();
    }
  }  
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Inner fragment - Moved to MainFragment.java
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Helper functions 
  ////////////////////////////////////////////////////////////////////////////////////////////////
  /** Switches the fragment back to the default. */
  private void setFragToDefault(){
    setFragmentAsContent(
        MainFragment.newInstance(inputMessage, false),
        MainFragment.TAG //as per doc.
        );
  }
  
  /** Adds/Replaces the content fragment and sets the currentFragmentTag
   * appropriately.
   * @param fragment The fragment to add.
   * @param tag The tag to use for findByTag.
   */
  private void setFragmentAsContent(Fragment fragment, String tag){
    if (currentFragmentTag.isEmpty()){
      getSupportFragmentManager().beginTransaction()
        .add( R.id.container, fragment, tag)
        .commit();      
    } else {
      getSupportFragmentManager().beginTransaction()
      .replace( R.id.container, fragment, tag)
      .commit();
    }
    currentFragmentTag = tag;
  }
      
  /** Starts or unsets the flashing fragment based upon boolean. 
   * @param add <code>true</code> adds it, <code>false</code> removes it
   * and resets to default.
   */
  private void startFlashFrag(boolean add){
    if (add){
      //swap in screen flash fragment.
      setFragmentAsContent( 
          ScreenFlashFragment.newInstance(),
          ScreenFlashFragment.class.getName()
          );
      setScreenBrightnessMax(true);
      setFullscreen(true);
    } else {
      //swap back in main.
      setFragToDefault();
      flashFrag = null;      
      setScreenBrightnessMax(false);
      setFullscreen(false);
    }    
  }
  /** Sets the activity screen brightness to max or default value.
   * @param max <code>true</code>: set to max, <code>false</code>: set to default.
   */
  private void setScreenBrightnessMax(boolean max){
    //FIXME Incompatible with "WindowManager.LayoutParams.FLAG_FULLSCREEN", which takes presedence 
    //May be niche bug as it was found on Samsung API10 device.
    WindowManager.LayoutParams lp = getWindow().getAttributes();
        lp.screenBrightness = max   ? WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL 
                      : WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
        getWindow().setAttributes(lp);
  }
  /**
   * Sets the activity to be fullscreen, removing the action bar and status
   * bar. Also sets isFullscreen to the value passed.
   * @param fullscreen <code>true</code> to set fullscreen,
   * <code>false</code> to return to normal screen.
   */
  private void setFullscreen(boolean fullscreen){
    Window window = getWindow();
    if (fullscreen){
      window.setFlags(
          WindowManager.LayoutParams.FLAG_FULLSCREEN, 
          WindowManager.LayoutParams.FLAG_FULLSCREEN);
      window.clearFlags(
          WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
      getSupportActionBar().hide();      
    } else {
      window.setFlags(
          WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, 
          WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
      window.clearFlags(
          WindowManager.LayoutParams.FLAG_FULLSCREEN);
      getSupportActionBar().show();
    }
    //ensure redraw
    findViewById(R.id.container).requestLayout();
    isFullscreen = fullscreen;
  }
  /**
   * Prompts for the signal action (taking into account UI threading).
   * @param state <code>true</code> for the on state, <code>false</code>
   * for off state.
   */
  private void signalAction(final boolean state){
    if (isMessageByFlashLight){
      try {
        if (maincameraFlashUtil != null){
          maincameraFlashUtil.flashLed(state);
        }
      } catch (IllegalStateException e){}
      
    } else {
      MainActivity.this.runOnUiThread(new Runnable(){
          public void run(){
            if (flashFrag != null){
              synchronized (flashFrag) {
                if (flashFrag != null) flashFrag.flashBackground(state);
              }
            }
          }
      });
      
    }    
  }
  /** Ends the message and returns to starting state. */
  private void endMessage(){
    morseTranscriber.cancel();
    if (!isMessageByFlashLight){
      MainActivity.this.runOnUiThread(new Runnable(){
          public void run(){
            startFlashFrag(false);
        }});
    } else {
      if (mainFrag != null){
        MainActivity.this.runOnUiThread(new Runnable(){
            public void run(){
              if (mainFrag != null){
                synchronized (mainFrag) {
                  if (mainFrag != null) mainFrag.setMessageComplete(true);
                }
              }
              
            }
          });
      }
    }
    isSending = false;
  }
  
  /** Gets boolean preference based on bool id. Default value is false. */
  private boolean getBoolPref(int boolKeyId) {
    return prefs.getBoolean(getResources().getString(boolKeyId), false);
  }
  
  /////////////////////////////////////////////////////////////////////////////////////////////////
  /// Implemented listeners
  ////////////////////////////////////////////////////////////////////////////////////////////////
  
  @Override
  public void onUpdateCameraFlashUtil(CameraFlashUtil cameraFlashUtil){
    maincameraFlashUtil = cameraFlashUtil;
    Log.d(CLASS_NAME, "onUpdateCameraFlashUtil");    
  }
  
  @Override
  public void onSendButton(String message) {
    if (isSending){
      morseTranscriber.cancel();
    }
      boolean loop  = 
        getBoolPref(R.string.com_ovrhere_morseflash_pref_KEY_LOOP_MESSAGE);
    isMessageByFlashLight = 
        getBoolPref(R.string.com_ovrhere_morseflash_pref_KEY_USE_CAMERA_FLASH);
    isSending = true;
    
    morseTranscriber.setMessage(message);
    morseTranscriber.setLoop(loop);
    inputMessage = message;
    
    if (!isMessageByFlashLight){
      startFlashFrag(true);
    } else {
      morseTranscriber.start();
    }
  }
  
  
  @Override  @Deprecated
  public void onSendButton(String message, boolean looped,
      boolean useCameraFlash) {
    onSendButton(message);
  }
  
  @Override
  public void onCancelButton() {
    //Overlapping interfaces, how urgent is this to fix?
    Log.d(CLASS_NAME, "onCancelButton");
    endMessage();
    
    if (currentFragmentTag.equals(MainFragment.TAG)){
      if (isMessageByFlashLight){
          isMessageByFlashLight = false;
        try {
          if (maincameraFlashUtil != null){
            maincameraFlashUtil.flashLed(false); 
          }
        } catch (IllegalStateException e){}
      }
    }    
  }
  
  @Override
  public void onFragmentViewLoaded() {
    if (!morseTranscriber.isRunning()){
      morseTranscriber.start();
    }
  }
  
  
  
  @Override
  public void onSignalStart() {
    Log.d(CLASS_NAME, "onSignalStart");
    signalAction(true);
  }
  
  @Override
  public void onSignalEnd() {
    Log.d(CLASS_NAME, "onSignalEnd");
    signalAction(false);
    if (!activityVisible){
      unseenSignals++;
      //if not visible, we count the unsceen ticks.
      if (unseenSignals > 3){
        //if over threshhold. cancel.
        if (morseTranscriber != null){
          morseTranscriber.cancel();
        }
      }
    }
  }
  
  @Override
  public void onMorseParsed() {
    // TODO Auto-generated method stub    
  }
  
  @Override
  public void onMorseCompleted() {
    Log.d(CLASS_NAME, "message complete");
    endMessage();    
  }

}




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