Java tutorial
/* * Copyright 2014 Jason J. * * 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 java.util.Random; import android.annotation.SuppressLint; 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 */ @SuppressLint("NewApi") 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_lib); 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(); } //Log.d(CLASS_NAME, "Send Button Clicked"); // } @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(); } }