Java tutorial
/*Copyright (C) 2018 M. Steve Todd mstevetodd@gmail.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package jmri.enginedriver; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.gesture.GestureOverlayView; import android.graphics.Typeface; import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.AudioManager; import android.media.ToneGenerator; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.CountDownTimer; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; import android.support.v4.app.FragmentActivity; import android.text.InputType; import android.text.format.Time; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.InputDevice; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.webkit.CookieSyncManager; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Random; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import eu.esu.mobilecontrol2.sdk.MobileControl2; import eu.esu.mobilecontrol2.sdk.StopButtonFragment; import eu.esu.mobilecontrol2.sdk.ThrottleFragment; import eu.esu.mobilecontrol2.sdk.ThrottleScale; import jmri.enginedriver.logviewer.ui.LogViewerActivity; import jmri.enginedriver.util.PermissionsHelper; import static android.view.InputDevice.getDevice; import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.ACTION_UP; //import static android.view.KeyEvent.KEYCODE_0; //import static android.view.KeyEvent.KEYCODE_5; import static android.view.KeyEvent.KEYCODE_A; import static android.view.KeyEvent.KEYCODE_BACK; //import static android.view.KeyEvent.KEYCODE_BUTTON_A; //import static android.view.KeyEvent.KEYCODE_BUTTON_B; //import static android.view.KeyEvent.KEYCODE_BUTTON_L1; //import static android.view.KeyEvent.KEYCODE_BUTTON_L2; //import static android.view.KeyEvent.KEYCODE_BUTTON_R1; //import static android.view.KeyEvent.KEYCODE_BUTTON_R2; //import static android.view.KeyEvent.KEYCODE_BUTTON_X; //import static android.view.KeyEvent.KEYCODE_BUTTON_Y; import static android.view.KeyEvent.KEYCODE_D; //import static android.view.KeyEvent.KEYCODE_DEL; //import static android.view.KeyEvent.KEYCODE_DPAD_DOWN; //import static android.view.KeyEvent.KEYCODE_DPAD_LEFT; //import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT; //import static android.view.KeyEvent.KEYCODE_DPAD_UP; //import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.KeyEvent.KEYCODE_F; import static android.view.KeyEvent.KEYCODE_N; import static android.view.KeyEvent.KEYCODE_R; //import static android.view.KeyEvent.KEYCODE_SPACE; import static android.view.KeyEvent.KEYCODE_T; import static android.view.KeyEvent.KEYCODE_V; import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; import static android.view.KeyEvent.KEYCODE_VOLUME_UP; import static android.view.KeyEvent.KEYCODE_W; import static android.view.KeyEvent.KEYCODE_X; //import static android.view.KeyEvent.KEYCODE_Z; // for changing the screen brightness // used for supporting Keyboard and Gamepad input; public class throttle extends FragmentActivity implements android.gesture.GestureOverlayView.OnGestureListener { ////public class throttle extends Activity implements android.gesture.GestureOverlayView.OnGestureListener { protected threaded_application mainapp; // hold pointer to mainapp protected SharedPreferences prefs; protected static final int MAX_SCREEN_THROTTLES = 3; // activity codes public static final int ACTIVITY_PREFS = 0; public static final int ACTIVITY_SELECT_LOCO = 1; public static final int ACTIVITY_CONSIST = 2; public static final int ACTIVITY_CONSIST_LIGHTS = 3; public static final int ACTIVITY_GAMEPAD_TEST = 4; private static final int GONE = 8; private static final int VISIBLE = 0; protected static final int throttleMargin = 8; // margin between the throttles in dp protected static final int titleBar = 45; // estimate of lost screen height in dp // speed scale factors public static final int MAX_SPEED_VAL_WIT = 126; // wit message maximum speed value, max speed slider value public static final int SPEED_STEP_CODE_14 = 8; // wit speed step codes public static final int SPEED_STEP_CODE_27 = 4; public static final int SPEED_STEP_CODE_28 = 2; public static final int SPEED_STEP_CODE_128 = 1; public static final int SPEED_STEP_AUTO_MODE = -1; // speed step pref value when set to AUTO mode protected static int[] maxSpeedSteps = { 100, 100, 100, 100, 100, 100 }; // decoder max speed steps protected static int max_throttle_change; // maximum allowable change of the sliders protected static int speedStepPref = 100; private static double[] displayUnitScales; // display units per slider count private static String VOLUME_INDICATOR = "v"; private static int[] GAMEPAD_INDICATOR = { 1, 2, 3 }; private static final String SELECTED_LOCO_INDICATOR_NONE = "None"; private static final String SELECTED_LOCO_INDICATOR_GAMEPAD = "Gamepad"; private static final String SELECTED_LOCO_INDICATOR_VOLUME = "Volume"; private static final String SELECTED_LOCO_INDICATOR_BOTH = "Both"; private String prefSelectedLocoIndicator = SELECTED_LOCO_INDICATOR_NONE; protected SeekBar[] sbs; // seekbars protected ViewGroup[] fbs; // function button tables protected Button[] bFwds; // buttons protected Button[] bStops; protected Button[] bRevs; protected Button[] bSels; protected Button[] bRSpds; protected Button[] bLSpds; protected TextView[] tvSpdLabs; // labels protected TextView[] tvSpdVals; protected TextView[] tvVols; // volume indicators protected TextView[] tvLeftDirInds; // direction indicators protected TextView[] tvRightDirInds; protected TextView[] tvGamePads; protected LinearLayout[] lls; // throttles protected LinearLayout[] llSetSpds; protected VerticalSeekBar[] vsbSpeeds; // SPDHT for Speed Id and Direction Button Heights protected LinearLayout[] llLocoIds; protected LinearLayout[] llLocoDirs; // SPDHT protected View vThrotScr; protected View vThrotScrWrap; private boolean stealPromptActive = false; //true while steal dialog is open protected boolean navigatingAway = false; // true if another activity was selected (false in onPause if going into background) private int whichVolume = 0; private int whichLastVolume = -1; private int whichLastGamepad1 = -1; // screen coordinates for throttle sliders, so we can ignore swipe on them protected int[] tops; protected int[] bottoms; // these are used for gesture tracking private float gestureStartX = 0; private float gestureStartY = 0; private boolean gestureFailed = false; // gesture didn't meet velocity or distance requirement private boolean gestureInProgress = false; // gesture is in progress private long gestureLastCheckTime; // time in milliseconds that velocity was last checked private static final long gestureCheckRate = 200; // rate in milliseconds to check velocity private VelocityTracker mVelocityTracker; private String FUNCTION_BUTTON_LOOK_FOR_WHISTLE = "WHISTLE"; private String FUNCTION_BUTTON_LOOK_FOR_HORN = "HORN"; private String FUNCTION_BUTTON_LOOK_FOR_BELL = "BELL"; private String FUNCTION_BUTTON_LOOK_FOR_HEAD = "HEAD"; private String FUNCTION_BUTTON_LOOK_FOR_LIGHT = "LIGHT"; private String FUNCTION_BUTTON_LOOK_FOR_REAR = "REAR"; private List<String> prefConsistFollowStrings; private List<String> prefConsistFollowActions; private List<Integer> prefConsistFollowHeadlights; private String prefConsistFollowDefaultAction = "none"; private static int FUNCTION_IS_LEAD_ONLY = 1; private static int FUNCTION_IS_TRAIL_ONLY = 2; private static int FUNCTION_IS_LEAD_AND_FOLLOW = 3; private static int FUNCTION_IS_LEAD_AND_TRAIL = 4; private static int FUNCTION_IS_FOLLOW = 5; private static int FUNCTION_ACTION_ON = 1; private static int FUNCTION_ACTION_OFF = 2; private static int FUNCTION_ACTION_TOGGLE = 3; private boolean prefSelectiveLeadSound = false; private boolean prefSelectiveLeadSoundF1 = false; private boolean prefSelectiveLeadSoundF2 = false; private static int BUTTON_PRESS_MESSAGE_TOGGLE = -1; private static int BUTTON_PRESS_MESSAGE_UP = 0; private static int BUTTON_PRESS_MESSAGE_DOWN = 1; private String prefConsistFollowRuleStyle = "original"; private static String CONSIST_FUNCTION_RULE_STYLE_ORIGINAL = "original"; private static String CONSIST_FUNCTION_RULE_STYLE_COMPLEX = "complex"; // private static String CONSIST_FUNCTION_ACTION_NONE = "none"; private static String CONSIST_FUNCTION_ACTION_LEAD = "lead"; private static String CONSIST_FUNCTION_ACTION_LEAD_AND_TRAIL = "lead and trail"; private static String CONSIST_FUNCTION_ACTION_ALL = "all"; private static String CONSIST_FUNCTION_ACTION_LEAD_EXACT = "lead exact"; private static String CONSIST_FUNCTION_ACTION_LEAD_AND_TRAIL_EXACT = "lead and trail exact"; private static String CONSIST_FUNCTION_ACTION_ALL_EXACT = "all exact"; // private static String CONSIST_FUNCTION_ACTION_SAME_F_NUMBER_LEAD = "f lead"; // private static String CONSIST_FUNCTION_ACTION_SAME_F_NUMBER_LEAD_AND_TRAIL = "f lead and trail"; // private static String CONSIST_FUNCTION_ACTION_SAME_F_NUMBER_ALL = "f all"; private static Integer CONSIST_FUNCTION_IS_HEADLIGHT = 1; private static Integer CONSIST_FUNCTION_IS_NOT_HEADLIGHT = 0; // function number-to-button maps protected LinkedHashMap<Integer, Button>[] functionMaps; // current direction private int[] dirs = { 1, 1, 1, 1, 1, 1 }; // requested direction for each throttle (single or multiple engines) protected static final String WEB_VIEW_LOCATION_NONE = "none"; protected static final String WEB_VIEW_LOCATION_BOTTOM = "Bottom"; protected static final String WEB_VIEW_LOCATION_TOP = "Top"; private String noUrl = "file:///android_asset/blank_page.html"; private static final float initialScale = 1.5f; protected WebView webView = null; protected String webViewLocation = WEB_VIEW_LOCATION_NONE; private static float scale = initialScale; // used to restore throttle web zoom level (after rotation) private static boolean clearHistory = false; // flags webViewClient to clear history when page load finishes private static String firstUrl = null; // desired first url when clearing history private static String currentUrl = null; private String currentTime = ""; private Menu TMenu; static int REP_DELAY = 25; private int prefSpeedButtonsSpeedStep = 4; private int prefVolumeSpeedButtonsSpeedStep = 1; private int prefGamePadSpeedButtonsSpeedStep = 4; private static final int SPEED_COMMAND_FROM_BUTTONS = 0; private static final int SPEED_COMMAND_FROM_VOLUME = 1; private static final int SPEED_COMMAND_FROM_GAMEPAD = 2; protected String speedButtonLeftText; protected String speedButtonRightText; protected String speedButtonUpText; protected String speedButtonDownText; private Handler repeatUpdateHandler = new Handler(); private boolean mAutoIncrement = false; private boolean mAutoDecrement = false; private static final int changeDelay = 1000; protected ChangeDelay[] changeTimers; protected boolean selectLocoRendered = false; // this will be true once set_labels() runs following rendering of the loco select textViews // used in the gesture for entering and exiting immersive mode private boolean immersiveModeIsOn; //used in the gesture for temporarily showing the Web View protected boolean webViewIsOn = false; private String prefSwipeUpOption; private String keepWebViewLocation = WEB_VIEW_LOCATION_NONE; // use for locking the screen on swipe up private boolean isScreenLocked = false; private boolean screenDimmed = false; private int screenBrightnessDim; private static final int SCREEN_BRIGHTNESS_MODE_MANUAL = 0; private static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; private static final String SWIPE_UP_OPTION_WEB = "Hide Web View"; private static final String SWIPE_UP_OPTION_LOCK = "Lock and Dim Screen"; private static final String SWIPE_UP_OPTION_DIM = "Dim Screen"; private static final String SWIPE_UP_OPTION_IMMERSIVE = "Immersive Mode"; //private int screenBrightnessBright; private int screenBrightnessOriginal; private int screenBrightnessModeOriginal; // used to hold the direction change preferences boolean dirChangeWhileMoving; boolean stopOnDirectionChange; boolean locoSelectWhileMoving; // used for the GamePad Support private static final String WHICH_GAMEPAD_MODE_NONE = "None"; private static final int DIRECTION_FORWARD = 1; private static final int DIRECTION_REVERSE = 0; // default to the iOS iCade mappings private String whichGamePadMode = WHICH_GAMEPAD_MODE_NONE; private static String PREF_GAMEPAD_BUTTON_OPTION_ALL_STOP = "All Stop"; private static String PREF_GAMEPAD_BUTTON_OPTION_STOP = "Stop"; private static String PREF_GAMEPAD_BUTTON_OPTION_NEXT_THROTTLE = "Next Throttle"; private static String PREF_GAMEPAD_BUTTON_OPTION_FORWARD = "Forward"; private static String PREF_GAMEPAD_BUTTON_OPTION_REVERSE = "Reverse"; private static String PREF_GAMEPAD_BUTTON_OPTION_FORWARD_REVERSE_TOGGLE = "Forward/Reverse Toggle"; private static String PREF_GAMEPAD_BUTTON_OPTION_INCREASE_SPEED = "Increase Speed"; private static String PREF_GAMEPAD_BUTTON_OPTION_DECREASE_SPEED = "Decrease Speed"; // Gamepad Button preferences private String[] prefGamePadButtons = { "Next Throttle", "Stop", "Function 00/Light", "Function 01/Bell", "Function 02/Horn", "Increase Speed", "Reverse", "Decrease Speed", "Forward", "All Stop", "Select", "Left Shoulder", "Right Shoulder", "Left Trigger", "Right Trigger" }; // none NextThr Speed+ Speed- Fwd Rev All Stop F2 F1 F0 Stop private int[] gamePadKeys = { 0, 0, KEYCODE_W, KEYCODE_X, KEYCODE_A, KEYCODE_D, KEYCODE_V, KEYCODE_T, KEYCODE_N, KEYCODE_R, KEYCODE_F, 0, 0, 0, 0, 0 }; private int[] gamePadKeys_Up = { 0, 0, KEYCODE_W, KEYCODE_X, KEYCODE_A, KEYCODE_D, KEYCODE_V, KEYCODE_T, KEYCODE_N, KEYCODE_R, KEYCODE_F, 0, 0, 0, 0, 0 }; // For TTS private int MY_TTS_DATA_CHECK_CODE = 1234; private static final String PREF_TTS_THROTTLE_RESPONSE_NONE = "None"; private static final String PREF_TTS_THROTTLE_RESPONSE_THROTTLE = "Throttle"; private static final String PREF_TTS_THROTTLE_RESPONSE_LOCO = "Loco"; private static final String PREF_TTS_THROTTLE_RESPONSE_SPEED = "Speed"; private static final String PREF_TTS_THROTTLE_RESPONSE_LOCO_SPEED = "LocoSpeed"; private TextToSpeech myTts; private String lastTts = "none"; private String prefTtsWhen = "None"; private String prefTtsThrottleResponse = "Throttle"; private boolean prefTtsGamepadTest = true; private boolean prefTtsGamepadTestComplete = true; private Time lastTtsTime; private static final String PREF_TT_WHEN_NONE = "None"; private static final String PREF_TT_WHEN_KEY = "Key"; private static final int TTS_MSG_VOLUME_THROTTLE = 1; private static final int TTS_MSG_GAMEPAD_THROTTLE = 2; private static final int TTS_MSG_GAMEPAD_GAMEPAD_TEST = 3; private static final int TTS_MSG_GAMEPAD_GAMEPAD_TEST_COMPLETE = 4; private static final int TTS_MSG_GAMEPAD_GAMEPAD_TEST_SKIPPED = 5; private static final int TTS_MSG_GAMEPAD_GAMEPAD_TEST_RESET = 6; private static final int TTS_MSG_GAMEPAD_GAMEPAD_TEST_FAIL = 7; private ToneGenerator tg; private Handler gamepadRepeatUpdateHandler = new Handler(); private boolean mGamepadAutoIncrement = false; private boolean mGamepadAutoDecrement = false; private Handler volumeKeysRepeatUpdateHandler = new Handler(); private boolean mVolumeKeysAutoIncrement = false; private boolean mVolumeKeysAutoDecrement = false; protected boolean prefDisableVolumeKeys = false; protected boolean prefVolumeKeysFollowLastTouchedThrottle = false; protected int layoutViewId; protected String prefThrottleScreenType; protected boolean prefThrottleViewImmersiveMode = false; protected boolean prefAlwaysUseDefaultFunctionLabels = false; protected int prefNumberOfDefaultFunctionLabels = 28; protected boolean prefDecreaseLocoNumberHeight = false; protected boolean pref_increase_slider_height_preference = false; protected boolean prefShowAddressInsteadOfName = false; protected boolean prefIncreaseWebViewSize = false; private int[] gamePadIds = { 0, 0, 0, 0, 0, 0 }; // which device id if assigned to each of the three throttles private int[] gamePadThrottleAssignment = { -1, -1, -1, -1, -1, -1 }; private boolean usingMultiplePads = false; private int[] gamePadDeviceIds = { 0, 0, 0, 0, 0, 0, 0 }; // which device ids have we seen private int[] gamePadDeviceIdsTested = { -1, -1, -1, -1, -1, -1, -1 }; // which device ids have we tested -1 = not tested 0 = test started 1 = test passed 2 = test failed private int gamepadCount = 0; // preference to chnage the consist's on long clicks boolean prefConsistLightsLongClick; public static final int LIGHT_FOLLOW = 1; protected boolean prefSwapForwardReverseButtons = false; protected boolean prefSwapForwardReverseButtonsLongPress = false; private boolean[] currentSwapForwardReverseButtons = { false, false, false, false, false, false }; protected boolean prefGamepadSwapForwardReverseWithScreenButtons = false; protected boolean prefGamepadTestEnforceTesting = true; private static final int GAMEPAD_TEST_PASS = 1; private static final int GAMEPAD_TEST_FAIL = 2; private static final int GAMEPAD_TEST_SKIPPED = 3; private static final int GAMEPAD_TEST_RESET = 9; private static final int GAMEPAD_GOOD = 1; private static final int GAMEPAD_BAD = 2; protected String DIRECTION_BUTTON_LEFT_TEXT = "Forward"; protected String DIRECTION_BUTTON_RIGHT_TEXT = "Reverse"; protected String[] FullLeftText = { DIRECTION_BUTTON_LEFT_TEXT, DIRECTION_BUTTON_LEFT_TEXT, DIRECTION_BUTTON_LEFT_TEXT, DIRECTION_BUTTON_LEFT_TEXT, DIRECTION_BUTTON_LEFT_TEXT, DIRECTION_BUTTON_LEFT_TEXT }; protected String[] FullRightText = { DIRECTION_BUTTON_RIGHT_TEXT, DIRECTION_BUTTON_RIGHT_TEXT, DIRECTION_BUTTON_RIGHT_TEXT, DIRECTION_BUTTON_RIGHT_TEXT, DIRECTION_BUTTON_RIGHT_TEXT, DIRECTION_BUTTON_RIGHT_TEXT }; protected String[] dirLeftIndicationText = { "", "", "", "", "", "" }; protected String[] dirRightIndicationText = { "", "", "", "", "", "" }; private static String GAMEPAD_FUNCTION_PREFIX = "Function "; protected String prefLeftDirectionButtons = DIRECTION_BUTTON_LEFT_TEXT; protected String prefRightDirectionButtons = DIRECTION_BUTTON_RIGHT_TEXT; protected int prefDirectionButtonLongPressDelay = 1000; private boolean isDirectionButtonLongPress; Handler directionButtonLongPressHandler = new Handler(); // The following are used for the shake detection private SensorManager sensorManager; private Sensor accelerometer; private shakeDetector shakeDetector; private static final String ACCELERATOROMETER_SHAKE_NONE = "None"; private static final String ACCELERATOROMETER_SHAKE_NEXT_V = "NextV"; private static final String ACCELERATOROMETER_SHAKE_DIM_SCREEN = "Dim"; private static final String ACCELERATOROMETER_SHAKE_LOCK_DIM_SCREEN = "LockDim"; private static final String ACCELERATOROMETER_SHAKE_WEB_VIEW = "Web"; private static final String ACCELERATOROMETER_SHAKE_ALL_STOP = "AllStop"; private String prefAccelerometerShake = ACCELERATOROMETER_SHAKE_NONE; private boolean accelerometerCurrent = false; protected static final String THEME_DEFAULT = "Default"; private static final int KIDS_TIMER_DISABLED = 0; private static final int KIDS_TIMER_STARTED = 1; private static final int KIDS_TIMER_ENABLED = 2; private static final int KIDS_TIMER_RUNNNING = 3; private static final int KIDS_TIMER_ENDED = 999; private static final String PREF_KIDS_TIMER_NONE = "0"; private static final String PREF_KIDS_TIMER_ENDED = "999"; private String prefKidsTimer = PREF_KIDS_TIMER_NONE; private int prefKidsTime = 0; // in milliseconds private int kidsTimerRunning = KIDS_TIMER_DISABLED; private MyCountDownTimer kidsTimer; private String prefKidsTimerResetPassword = "9999"; private String prefKidsTimerRestartPassword = "0000"; private String passwordText = ""; // For ESU MobileControlII private static final boolean IS_ESU_MCII = MobileControl2.isMobileControl2(); private boolean isEsuMc2Stopped = false; // for tracking status of Stop button private boolean isEsuMc2AllStopped = false; // for tracking if all throttles stopped private ThrottleFragment esuThrottleFragment; private StopButtonFragment esuStopButtonFragment; private EsuMc2LedControl esuMc2Led = new EsuMc2LedControl(); private Handler esuButtonRepeatUpdateHandler = new Handler(); private boolean esuButtonAutoIncrement = false; private boolean esuButtonAutoDecrement = false; private boolean prefEsuMc2EndStopDirectionChange = true; private boolean prefEsuMc2StopButtonShortPress = true; private boolean isEsuMc2KnobEnabled = true; // Create default ESU MCII ThrottleScale for each throttle private ThrottleScale[] esuThrottleScales = { new ThrottleScale(10, 127), new ThrottleScale(10, 127), new ThrottleScale(10, 127), new ThrottleScale(10, 127), new ThrottleScale(10, 127), new ThrottleScale(10, 127) }; public ImportExportPreferences importExportPreferences = new ImportExportPreferences(); private static final int FORCED_RESTART_REASON_IMPORT_SERVER_AUTO = 7; private static final String EXTERNAL_PREFERENCES_IMPORT_FILENAME = "auto_preferences.ed"; private static final String ENGINE_DRIVER_DIR = "engine_driver"; private static final String PREF_IMPORT_ALL_FULL = "Yes"; private static final String PREF_IMPORT_ALL_PARTIAL = "No"; private static final String PREF_IMPORT_ALL_RESET = "-"; private enum EsuMc2Led { RED(MobileControl2.LED_RED), GREEN(MobileControl2.LED_GREEN); private int value; EsuMc2Led(int value) { this.value = value; } private int getValue() { return value; } } private enum EsuMc2LedState { OFF, ON, QUICK_FLASH, STEADY_FLASH, LONG_FLASH, SHORT_FLASH } private enum EsuMc2ButtonAction { NO_ACTION("(no function)"), ALL_STOP("All Stop"), STOP("Stop"), DIRECTION_FORWARD( "Forward"), DIRECTION_REVERSE("Reverse"), DIRECTION_TOGGLE( "Forward/Reverse Toggle"), SPEED_INCREASE("Increase Speed"), SPEED_DECREASE( "Decrease Speed"), NEXT_THROTTLE("Next Throttle"), FN00("Function 00/Light", 0), FN01("Function 01/Bell", 1), FN02("Function 02/Horn", 2), FN03( "Function 03", 3), FN04("Function 04", 4), FN05("Function 05", 5), FN06( "Function 06", 6), FN07("Function 07", 7), FN08("Function 08", 8), FN09( "Function 09", 9), FN10("Function 10", 10), FN11("Function 11", 11), FN12("Function 12", 12), FN13( "Function 13", 13), FN14("Function 14", 14), FN15( "Function 15", 15), FN16("Function 16", 16), FN17( "Function 17", 17), FN18( "Function 18", 18), FN19( "Function 19", 19), FN20( "Function 20", 20), FN21( "Function 21", 21), FN22( "Function 22", 22), FN23( "Function 23", 23), FN24( "Function 24", 24), FN25( "Function 25", 25), FN26( "Function 26", 26), FN27( "Function 27", 27), FN28( "Function 28", 28); private String action; private int function; private static final Map<String, EsuMc2ButtonAction> ENUM_MAP; EsuMc2ButtonAction(String action) { this(action, -1); } EsuMc2ButtonAction(String action, int function) { this.action = action; this.function = function; } private String getAction() { return this.action; } private int getFunction() { return this.function; } // Build immutable map of String name to enum pairs static { Map<String, EsuMc2ButtonAction> map = new ConcurrentHashMap<>(); for (EsuMc2ButtonAction action : EsuMc2ButtonAction.values()) { map.put(action.getAction(), action); } ENUM_MAP = Collections.unmodifiableMap(map); } private static EsuMc2ButtonAction getAction(String action) { return ENUM_MAP.get(action); } } private static class EsuMc2LedControl { EsuMc2LedState stateRed; EsuMc2LedState stateGreen; private void setState(EsuMc2Led which, EsuMc2LedState state) { this.setState(which, state, false); } private void setState(EsuMc2Led which, EsuMc2LedState state, boolean storeState) { switch (state) { case OFF: MobileControl2.setLedState(which.getValue(), false); break; case ON: MobileControl2.setLedState(which.getValue(), true); break; case QUICK_FLASH: MobileControl2.setLedState(which.getValue(), 125, 125); break; case STEADY_FLASH: MobileControl2.setLedState(which.getValue(), 250, 250); break; case LONG_FLASH: MobileControl2.setLedState(which.getValue(), 375, 125); break; case SHORT_FLASH: MobileControl2.setLedState(which.getValue(), 125, 375); break; default: // Default off MobileControl2.setLedState(which.getValue(), false); } if (storeState) { switch (which) { case RED: stateRed = state; break; case GREEN: stateGreen = state; break; } } } private EsuMc2LedState getState(EsuMc2Led which) { if (which == EsuMc2Led.RED) { return this.stateRed; } else { return this.stateGreen; } } private void revertLEDStates() { revertLEDState(EsuMc2Led.RED); revertLEDState(EsuMc2Led.GREEN); } private void revertLEDState(EsuMc2Led which) { setState(which, getState(which), false); } } // For speed slider speed buttons. private class RptUpdater implements Runnable { int whichThrottle; private RptUpdater(int WhichThrottle) { whichThrottle = WhichThrottle; try { REP_DELAY = Integer.parseInt(prefs.getString("speed_arrows_throttle_repeat_delay", "100")); } catch (NumberFormatException ex) { REP_DELAY = 100; } } @Override public void run() { if (mAutoIncrement) { incrementSpeed(whichThrottle, SPEED_COMMAND_FROM_BUTTONS); repeatUpdateHandler.postDelayed(new RptUpdater(whichThrottle), REP_DELAY); } else if (mAutoDecrement) { decrementSpeed(whichThrottle, SPEED_COMMAND_FROM_BUTTONS); repeatUpdateHandler.postDelayed(new RptUpdater(whichThrottle), REP_DELAY); } } } private class MyCountDownTimer extends CountDownTimer { public MyCountDownTimer(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.KIDS_TIMER_START, "", 0, 0); } @Override public void onFinish() { // When timer is finished mainapp.sendMsg(mainapp.comm_msg_handler, message_type.KIDS_TIMER_END, "", 0, 0); //finish(); //showTimerPasswordDialog(); } @Override public void onTick(long millisUntilFinished) { // millisUntilFinished The amount of time until finished. int secondsLeft = (int) millisUntilFinished / 1000; mainapp.sendMsg(mainapp.comm_msg_handler, message_type.KIDS_TIMER_TICK, "", secondsLeft, 0); } } @SuppressLint("ApplySharedPref") private void kidsTimerActions(int action, int arg) { switch (action) { case KIDS_TIMER_DISABLED: kidsTimerRunning = KIDS_TIMER_DISABLED; for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { enable_disable_buttons(throttleIndex, false); } setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle)); if (TMenu != null) { mainapp.setKidsMenuOptions(TMenu, true, 0); } break; case KIDS_TIMER_ENABLED: if (((prefKidsTime > 0) && (kidsTimerRunning != KIDS_TIMER_RUNNNING))) { kidsTimerRunning = KIDS_TIMER_ENABLED; for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { enable_disable_buttons(throttleIndex, false); bSels[throttleIndex].setEnabled(false); } setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle_kids_enabled)); if (TMenu != null) { mainapp.setKidsMenuOptions(TMenu, false, 0); } } break; case KIDS_TIMER_STARTED: if ((prefKidsTime > 0) && (kidsTimerRunning != KIDS_TIMER_RUNNNING)) { kidsTimerRunning = KIDS_TIMER_RUNNNING; kidsTimer = new MyCountDownTimer(prefKidsTime, 1000); kidsTimer.start(); } break; case KIDS_TIMER_ENDED: speedUpdateAndNotify(0); kidsTimerRunning = KIDS_TIMER_ENDED; kidsTimer.cancel(); for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { enable_disable_buttons(throttleIndex, true); bSels[throttleIndex].setEnabled(false); } prefs.edit().putString("prefKidsTimer", PREF_KIDS_TIMER_ENDED).commit(); //reset the preference setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle_kids_ended)); //if (TMenu != null) { // mainapp.setKidsMenuOptions(TMenu, true, gamepadCount); //} break; case KIDS_TIMER_RUNNNING: for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { bSels[throttleIndex].setEnabled(false); } setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle_kids_running) .replace("%1$s", Integer.toString(arg))); break; } } // Handle messages from the communication thread TO this thread (responses from withrottle) @SuppressLint("HandlerLeak") private class throttle_handler extends Handler { @Override public void handleMessage(Message msg) { String response_str = msg.obj.toString(); // Log.d("Engine_Driver", "throttle handleMessage " + response_str ); switch (msg.what) { case message_type.RESPONSE: { // handle messages from WiThrottle server if (response_str.length() < 2) return; //bail if too short, to avoid crash char com1 = response_str.charAt(0); int whichThrottle = mainapp.throttleCharToInt(response_str.charAt(1)); // '0', '1'',2' etc. switch (com1) { // various MultiThrottle messages case 'M': char com2 = response_str.charAt(2); String[] ls = threaded_application.splitByString(response_str, "<;>"); String addr; try { addr = ls[0].substring(3); } catch (Exception e) { addr = ""; } if ((whichThrottle >= 0) && (whichThrottle < mainapp.numThrottles)) { if (com2 == '+' || com2 == 'L') { // if loco added or function labels updated if (com2 == ('+')) { // set_default_function_labels(); enable_disable_buttons(whichThrottle); // direction and slider: pass whichthrottle } // loop through all function buttons and set label and dcc functions (based on settings) or hide if no label set_function_labels_and_listeners_for_view(whichThrottle); enable_disable_buttons_for_view(fbs[whichThrottle], true); set_labels(); } else if (com2 == '-') { // if loco removed removeLoco(whichThrottle); swapToNextAvilableThrottleForGamePad(whichThrottle, true); // see if we can/need to move the gamepad to another throttle gamePadIds[whichThrottle] = 0; gamePadThrottleAssignment[whichThrottle] = -1; } else if (com2 == 'A') { // e.g. MTAL2608<;>R1 char com3 = ' '; if (ls.length >= 2) { //make sure there's a value to parse com3 = ls[1].charAt(0); } if (com3 == 'R') { //MTAL5511<;>R0 int dir; try { dir = Integer.valueOf(ls[1].substring(1, 2)); } catch (Exception e) { dir = 1; } Consist con; int curDir = dirs[whichThrottle]; con = mainapp.consists[whichThrottle]; if (addr.equals(con.getLeadAddr())) { if (dir != curDir) { // lead/consist direction changed from outside of ED showDirectionRequest(whichThrottle, dir); // update requested direction indication setEngineDirection(whichThrottle, dir, true); // update rest of consist to match new direction } } else { int locoDir = curDir; // calc correct direction for this (non-lead) loco try { if (con.isReverseOfLead(addr)) locoDir ^= 1; if (locoDir != dir) {// if loco has wrong direction then correct it mainapp.sendMsg(mainapp.comm_msg_handler, message_type.DIRECTION, addr, whichThrottle, locoDir); } } catch (Exception e) { // isReverseOfLead returns null if addr is not in con // - should not happen unless WiT is reporting on engine user just dropped from ED consist? Log.d("Engine_Driver", "throttle " + whichThrottle + " loco " + addr + " direction reported by WiT but engine is not assigned"); } } } else if (com3 == 'V') { try { int speed = Integer.parseInt(ls[1].substring(1)); speedUpdateWiT(whichThrottle, speed); // update speed slider and indicator } catch (Exception ignored) { } } else if (com3 == 'F') { try { int function = Integer.valueOf(ls[1].substring(2)); String loco = ls[0].substring(3); Consist con = mainapp.consists[whichThrottle]; if ((prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) || (loco.equals(con.getLeadAddr()))) { //if using the 'complex' follow function rules, only send it to the lead loco set_function_state(whichThrottle, function); } } catch (Exception ignored) { } } else if (com3 == 's') { try { int speedStepCode = Integer.valueOf(ls[1].substring(1)); setSpeedStepsFromWiT(whichThrottle, speedStepCode); } catch (Exception ignored) { } } } } break; case '0': case '1': case '2': case '3': case '4': case '5': enable_disable_buttons(com1); // pass whichthrottle set_labels(); break; case 'R': if (whichThrottle >= 0 && whichThrottle <= mainapp.numThrottles) { set_function_labels_and_listeners_for_view(whichThrottle); enable_disable_buttons_for_view(fbs[whichThrottle], true); set_labels(); } else { try { String scom2 = response_str.substring(1, 6); if (scom2.equals("PF}|{")) { whichThrottle = mainapp.throttleCharToInt(response_str.charAt(6)); set_all_function_states(whichThrottle); } } catch (IndexOutOfBoundsException ignored) { } } break; case 'P': // panel info // if (whichThrottle == 'W') { // PW - web server port info if (whichThrottle == 39) { // PW - web server port info initWeb(); set_labels(); } // if (whichThrottle == 'P') { // PP - power state change if (whichThrottle == 32) { // PP - power state change set_labels(); } break; } // end of switch if (!selectLocoRendered) // call set_labels if the select loco textViews had not rendered the last time it was called set_labels(); } break; case message_type.ROSTER_UPDATE: set_labels(); // refresh function labels when any roster response is received break; case message_type.WIT_CON_RETRY: witRetry(response_str); break; case message_type.WIT_CON_RECONNECT: break; case message_type.CURRENT_TIME: currentTime = response_str; setTitle(); break; case message_type.DISCONNECT: case message_type.SHUTDOWN: disconnect(); break; case message_type.WEBVIEW_LOC: // webview location changed // set new location webViewLocation = prefs.getString("WebViewLocation", getApplicationContext().getResources().getString(R.string.prefWebViewLocationDefaultValue)); keepWebViewLocation = webViewLocation; webViewIsOn = false; reloadWeb(); break; case message_type.INITIAL_WEBPAGE: initWeb(); break; case message_type.REQ_STEAL: promptForSteal(msg.obj.toString(), msg.arg1); break; case message_type.KIDS_TIMER_ENABLE: kidsTimerActions(KIDS_TIMER_ENABLED, 0); break; case message_type.KIDS_TIMER_START: kidsTimerActions(KIDS_TIMER_STARTED, 0); break; case message_type.KIDS_TIMER_TICK: kidsTimerActions(KIDS_TIMER_RUNNNING, msg.arg1); break; case message_type.KIDS_TIMER_END: kidsTimerActions(KIDS_TIMER_ENDED, 0); break; case message_type.IMPORT_SERVER_AUTO_AVAILABLE: Log.d("Engine_Driver", "throttle handleMessage AUTO_IMPORT_URL_AVAILABLE " + response_str); autoImportUrlAskToImport(); break; } } } // Change the screen brightness public void setScreenBrightness(int brightnessValue) { Context mContext; mContext = getApplicationContext(); /* public abstract ContentResolver getContentResolver () Return a ContentResolver instance for your application's package. */ /* Settings The Settings provider contains global system-level device preferences. Settings.System System settings, containing miscellaneous system preferences. This table holds simple name/value pairs. There are convenience functions for accessing individual settings entries. */ /* public static final String SCREEN_BRIGHTNESS The screen backlight brightness between 0 and 255. Constant Value: "screen_brightness" */ /* public static boolean putInt (ContentResolver cr, String name, int value) Convenience function for updating a single settings value as an integer. This will either create a new entry in the table if the given name does not exist, or modify the value of the existing row with that name. Note that internally setting values are always stored as strings, so this function converts the given value to a string before storing it. Parameters cr : The ContentResolver to access. name : The name of the setting to modify. value : The new value for the setting. Returns true : if the value was set, false on database errors */ // Make sure brightness value between 0 to 255 if (brightnessValue >= 0 && brightnessValue <= 255) { if (mainapp.androidVersion >= mainapp.minScreenDimNewMethodVersion) { setScreenBrightnessMode(SCREEN_BRIGHTNESS_MODE_MANUAL); if (!PermissionsHelper.getInstance().isPermissionGranted(throttle.this, PermissionsHelper.WRITE_SETTINGS)) { PermissionsHelper.getInstance().requestNecessaryPermissions(throttle.this, PermissionsHelper.WRITE_SETTINGS); } try { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, brightnessValue); Log.d("Engine_Driver", "screen brightness successfully changed to " + brightnessValue); } catch (Exception e) { Log.e("Engine_Driver", "screen brightness was NOT changed to " + brightnessValue); } } else { WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.screenBrightness = ((float) brightnessValue) / 255; getWindow().setAttributes(lp); } } } // Get the screen current brightness protected int getScreenBrightness() { Context mContext; mContext = getApplicationContext(); /* public static int getInt (ContentResolver cr, String name, int def) Convenience function for retrieving a single system settings value as an integer. Note that internally setting values are always stored as strings; this function converts the string to an integer for you. The default value will be returned if the setting is not defined or not an integer. Parameters cr : The ContentResolver to access. name : The name of the setting to retrieve. def : Value to return if the setting is not defined. Returns The setting's current value, or 'def' if it is not defined or not a valid integer. */ // int brightnessValue = Settings.System.getInt( // mContext.getContentResolver(), // Settings.System.SCREEN_BRIGHTNESS, // 0 // ); // return brightnessValue; return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0); } public void setScreenBrightnessMode(int brightnessModeValue) { Context mContext; mContext = getApplicationContext(); if (mainapp.androidVersion >= mainapp.minScreenDimNewMethodVersion) { if (brightnessModeValue >= 0 && brightnessModeValue <= 1) { if (!PermissionsHelper.getInstance().isPermissionGranted(throttle.this, PermissionsHelper.WRITE_SETTINGS)) { PermissionsHelper.getInstance().requestNecessaryPermissions(throttle.this, PermissionsHelper.WRITE_SETTINGS); } try { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, brightnessModeValue); } catch (Exception e) { Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastUnableToSetBrightness), Toast.LENGTH_SHORT).show(); } } } } protected int getScreenBrightnessMode() { Context mContext; mContext = getApplicationContext(); int BrightnessModeValue = 0; if (mainapp.androidVersion >= mainapp.minScreenDimNewMethodVersion) { BrightnessModeValue = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, 0); } return BrightnessModeValue; } protected void setImmersiveModeOn(View webView) { immersiveModeIsOn = false; if (prefThrottleViewImmersiveMode) { // if the preference is set use Immersive mode if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { immersiveModeIsOn = true; webView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); } } } protected void setImmersiveModeOff(View webView) { immersiveModeIsOn = false; if (prefThrottleViewImmersiveMode) { // if the preference is set use Immersive mode if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { webView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } webView.invalidate(); } } private boolean directionButtonsAreCurrentlyReversed(int throttleIndexNo) { boolean isOk = false; if (((!prefSwapForwardReverseButtons) && (currentSwapForwardReverseButtons[throttleIndexNo])) || (((prefSwapForwardReverseButtons) && (!currentSwapForwardReverseButtons[throttleIndexNo])))) { isOk = true; } return isOk; } private boolean gamepadDirectionButtonsAreCurrentlyReversed(int throttleIndexNo) { boolean isOk = false; if ((prefGamepadSwapForwardReverseWithScreenButtons) && (((currentSwapForwardReverseButtons[throttleIndexNo]) && (!prefSwapForwardReverseButtons)) || ((!currentSwapForwardReverseButtons[throttleIndexNo]) && (prefSwapForwardReverseButtons)))) { isOk = true; } return isOk; } // set or restore the screen brightness when used for the Swipe Up or Shake private void setRestoreScreenDim(String toastMsg) { if (screenDimmed) { screenDimmed = false; setScreenBrightness(screenBrightnessOriginal); setScreenBrightnessMode(screenBrightnessModeOriginal); } else { screenDimmed = true; Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_SHORT).show(); screenBrightnessOriginal = getScreenBrightness(); setScreenBrightness(screenBrightnessDim); } } // set or restore the screen brightness and lock or unlock the sceen when used for the Swipe Up or Shake private void setRestoreScreenLockDim(String toastMsg) { if (isScreenLocked) { isScreenLocked = false; Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastThrottleScreenUnlocked), Toast.LENGTH_SHORT).show(); setScreenBrightness(screenBrightnessOriginal); setScreenBrightnessMode(screenBrightnessModeOriginal); } else { isScreenLocked = true; Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_SHORT).show(); screenBrightnessOriginal = getScreenBrightness(); setScreenBrightness(screenBrightnessDim); } } private void setupSensor() { if (!prefAccelerometerShake.equals(ACCELERATOROMETER_SHAKE_NONE)) { // ShakeDetector initialization sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (accelerometer != null) { shakeDetector = new shakeDetector(getApplicationContext()); shakeDetector.setOnShakeListener(new shakeDetector.OnShakeListener() { @Override public void onShake(int count) { switch (prefAccelerometerShake) { case ACCELERATOROMETER_SHAKE_WEB_VIEW: if ((webViewLocation.equals(WEB_VIEW_LOCATION_NONE)) && (keepWebViewLocation.equals(WEB_VIEW_LOCATION_NONE))) { GamepadFeedbackSound(true); Toast.makeText(getApplicationContext(), getApplicationContext().getResources() .getString(R.string.toastShakeWebViewUnavailable), Toast.LENGTH_SHORT) .show(); } else { GamepadFeedbackSound(false); showHideWebView(getApplicationContext().getResources() .getString(R.string.toastShakeWebViewHidden)); } break; case ACCELERATOROMETER_SHAKE_NEXT_V: GamepadFeedbackSound(false); setNextActiveThrottle(); speakWords(TTS_MSG_VOLUME_THROTTLE, whichVolume); break; case ACCELERATOROMETER_SHAKE_LOCK_DIM_SCREEN: GamepadFeedbackSound(false); setRestoreScreenLockDim(getApplicationContext().getResources() .getString(R.string.toastShakeScreenLocked)); break; case ACCELERATOROMETER_SHAKE_DIM_SCREEN: GamepadFeedbackSound(false); setRestoreScreenDim(getApplicationContext().getResources() .getString(R.string.toastShakeScreenDimmed)); break; case ACCELERATOROMETER_SHAKE_ALL_STOP: GamepadFeedbackSound(false); speedUpdateAndNotify(0); // update all three throttles } } }); accelerometerCurrent = true; } else { Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastAccelerometerNotFound), Toast.LENGTH_LONG).show(); } } } protected void addConsistFollowRule(String consistFollowString, String consistFollowAction, Integer consistFollowHeadlight) { if (consistFollowString.trim().length() > 0) { String[] prefConsistFollowStringstemp = threaded_application.splitByString(consistFollowString, ","); for (int i = 0; i < prefConsistFollowStringstemp.length; i++) { prefConsistFollowStrings.add(prefConsistFollowStringstemp[i].trim()); prefConsistFollowActions.add(consistFollowAction); prefConsistFollowHeadlights.add(consistFollowHeadlight); } } } protected void getKidsTimerPrefs() { prefKidsTimer = prefs.getString("prefKidsTimer", getResources().getString(R.string.prefKidsTimerDefaultValue)); if ((!prefKidsTimer.equals(PREF_KIDS_TIMER_NONE)) && (!prefKidsTimer.equals(PREF_KIDS_TIMER_ENDED))) { prefKidsTime = Integer.parseInt(prefKidsTimer) * 60000; } else { prefKidsTime = 0; } prefKidsTimerResetPassword = prefs.getString("prefKidsTimerResetPassword", getResources().getString(R.string.prefKidsTimerResetPasswordDefaultValue)); prefKidsTimerRestartPassword = prefs.getString("prefKidsTimerRestartPassword", getResources().getString(R.string.prefKidsTimerRestartPasswordDefaultValue)); } // get all the preferences that should be read when the activity is created or resumes @SuppressLint("ApplySharedPref") protected void getCommonPrefs(boolean isCreate) { if (isCreate) { //only do onCreate webViewLocation = prefs.getString("WebViewLocation", getApplicationContext().getResources().getString(R.string.prefWebViewLocationDefaultValue)); } // DIRECTION_BUTTON_LEFT_TEXT = getApplicationContext().getResources().getString(R.string.prefLeftDirectionButtonsDefaultValue).trim(); // DIRECTION_BUTTON_RIGHT_TEXT = getApplicationContext().getResources().getString(R.string.prefRightDirectionButtonsDefaultValue).trim(); prefDirectionButtonLongPressDelay = preferences.getIntPrefValue(prefs, "prefDirectionButtonLongPressDelay", getApplicationContext().getResources() .getString(R.string.prefDirectionButtonLongPressDelayDefaultValue)); FUNCTION_BUTTON_LOOK_FOR_WHISTLE = getApplicationContext().getResources() .getString(R.string.functionButtonLookForWhistle).trim(); FUNCTION_BUTTON_LOOK_FOR_HORN = getApplicationContext().getResources() .getString(R.string.functionButtonLookForHorn).trim(); FUNCTION_BUTTON_LOOK_FOR_BELL = getApplicationContext().getResources() .getString(R.string.functionButtonLookForBell).trim(); FUNCTION_BUTTON_LOOK_FOR_HEAD = getApplicationContext().getResources() .getString(R.string.functionButtonLookForHead).trim(); FUNCTION_BUTTON_LOOK_FOR_LIGHT = getApplicationContext().getResources() .getString(R.string.functionButtonLookForLight).trim(); FUNCTION_BUTTON_LOOK_FOR_REAR = getApplicationContext().getResources() .getString(R.string.functionButtonLookForRear).trim(); prefConsistFollowDefaultAction = prefs.getString("prefConsistFollowDefaulAction", getApplicationContext() .getResources().getString(R.string.prefConsistFollowDefaultActionDefaultValue)); prefConsistFollowStrings = new ArrayList<>(); prefConsistFollowActions = new ArrayList<>(); prefConsistFollowHeadlights = new ArrayList<>(); prefConsistFollowRuleStyle = prefs.getString("prefConsistFollowRuleStyle", getApplicationContext().getResources().getString(R.string.prefConsistFollowRuleStyleDefaultValue)); String prefConsistFollowStringtemp = prefs.getString("prefConsistFollowString1", getApplicationContext().getResources().getString(R.string.prefConsistFollowString1DefaultValue)); String prefConsistFollowActiontemp = prefs.getString("prefConsistFollowAction1", getApplicationContext().getResources().getString(R.string.prefConsistFollowString1DefaultValue)); addConsistFollowRule(prefConsistFollowStringtemp, prefConsistFollowActiontemp, CONSIST_FUNCTION_IS_HEADLIGHT); prefConsistFollowStringtemp = prefs.getString("prefConsistFollowString2", getApplicationContext().getResources().getString(R.string.prefConsistFollowString2DefaultValue)); prefConsistFollowActiontemp = prefs.getString("prefConsistFollowAction2", getApplicationContext().getResources().getString(R.string.prefConsistFollowString2DefaultValue)); addConsistFollowRule(prefConsistFollowStringtemp, prefConsistFollowActiontemp, CONSIST_FUNCTION_IS_NOT_HEADLIGHT); prefConsistFollowStringtemp = prefs.getString("prefConsistFollowString3", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); prefConsistFollowActiontemp = prefs.getString("prefConsistFollowAction3", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); addConsistFollowRule(prefConsistFollowStringtemp, prefConsistFollowActiontemp, CONSIST_FUNCTION_IS_NOT_HEADLIGHT); prefConsistFollowStringtemp = prefs.getString("prefConsistFollowString4", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); prefConsistFollowActiontemp = prefs.getString("prefConsistFollowAction4", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); addConsistFollowRule(prefConsistFollowStringtemp, prefConsistFollowActiontemp, CONSIST_FUNCTION_IS_NOT_HEADLIGHT); prefConsistFollowStringtemp = prefs.getString("prefConsistFollowString5", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); prefConsistFollowActiontemp = prefs.getString("prefConsistFollowAction5", getApplicationContext() .getResources().getString(R.string.prefConsistFollowStringOtherDefaultValue)); addConsistFollowRule(prefConsistFollowStringtemp, prefConsistFollowActiontemp, CONSIST_FUNCTION_IS_NOT_HEADLIGHT); // increase height of throttle slider (if requested in preferences) pref_increase_slider_height_preference = prefs.getBoolean("increase_slider_height_preference", getResources().getBoolean(R.bool.prefIncreaseSliderHeightDefaultValue)); // decrease height of Loco Id (if requested in preferences) prefDecreaseLocoNumberHeight = prefs.getBoolean("prefDecreaseLocoNumberHeight", getResources().getBoolean(R.bool.prefDecreaseLocoNumberHeightDefaultValue)); // increase the web view height if the preference is set prefIncreaseWebViewSize = prefs.getBoolean("prefIncreaseWebViewSize", getResources().getBoolean(R.bool.prefIncreaseWebViewSizeDefaultValue)); prefThrottleViewImmersiveMode = prefs.getBoolean("prefThrottleViewImmersiveMode", getResources().getBoolean(R.bool.prefThrottleViewImmersiveModeDefaultValue)); prefShowAddressInsteadOfName = prefs.getBoolean("prefShowAddressInsteadOfName", getResources().getBoolean(R.bool.prefShowAddressInsteadOfNameDefaultValue)); prefSwipeUpOption = prefs.getString("SwipeUpOption", getApplicationContext().getResources().getString(R.string.prefSwipeUpOptionDefaultValue)); isScreenLocked = false; dirChangeWhileMoving = prefs.getBoolean("DirChangeWhileMovingPreference", getResources().getBoolean(R.bool.prefDirChangeWhileMovingDefaultValue)); stopOnDirectionChange = prefs.getBoolean("prefStopOnDirectionChange", getResources().getBoolean(R.bool.prefStopOnDirectionChangeDefaultValue)); locoSelectWhileMoving = prefs.getBoolean("SelLocoWhileMovingPreference", getResources().getBoolean(R.bool.prefSelLocoWhileMovingDefaultValue)); screenBrightnessDim = Integer.parseInt(prefs.getString("prefScreenBrightnessDim", getResources().getString(R.string.prefScreenBrightnessDimDefaultValue))) * 255 / 100; //screenBrightnessBright = Integer.parseInt(prefs.getString("prefScreenBrightnessBright", getResources().getString(R.string.prefScreenBrightnessBrightDefaultValue))) * 255 /100; prefConsistLightsLongClick = prefs.getBoolean("ConsistLightsLongClickPreference", getResources().getBoolean(R.bool.prefConsistLightsLongClickDefaultValue)); prefGamepadTestEnforceTesting = prefs.getBoolean("prefGamepadTestEnforceTesting", getResources().getBoolean(R.bool.prefGamepadTestEnforceTestingDefaultValue)); prefDisableVolumeKeys = prefs.getBoolean("prefDisableVolumeKeys", getResources().getBoolean(R.bool.prefDisableVolumeKeysDefaultValue)); prefSelectedLocoIndicator = prefs.getString("prefSelectedLocoIndicator", getResources().getString(R.string.prefSelectedLocoIndicatorDefaultValue)); prefVolumeKeysFollowLastTouchedThrottle = prefs.getBoolean("prefVolumeKeysFollowLastTouchedThrottle", getResources().getBoolean(R.bool.prefVolumeKeysFollowLastTouchedThrottleDefaultValue)); // Ignore the labels for the loco in the Roster and use the defaults... if requested in preferences prefAlwaysUseDefaultFunctionLabels = prefs.getBoolean("prefAlwaysUseDefaultFunctionLabels", getResources().getBoolean(R.bool.prefAlwaysUseDefaultFunctionLabelsDefaultValue)); prefNumberOfDefaultFunctionLabels = Integer.parseInt(prefs.getString("prefNumberOfDefaultFunctionLabels", getResources().getString(R.string.prefNumberOfDefaultFunctionLabelsDefaultValue))); prefAccelerometerShake = prefs.getString("prefAccelerometerShake", getApplicationContext().getResources().getString(R.string.prefAccelerometerShakeDefaultValue)); // set speed buttons speed step prefSpeedButtonsSpeedStep = preferences.getIntPrefValue(prefs, "speed_arrows_throttle_speed_step", "4"); prefVolumeSpeedButtonsSpeedStep = preferences.getIntPrefValue(prefs, "prefVolumeSpeedButtonsSpeedStep", getApplicationContext().getResources() .getString(R.string.prefVolumeSpeedButtonsSpeedStepDefaultValue)); prefGamePadSpeedButtonsSpeedStep = preferences.getIntPrefValue(prefs, "prefGamePadSpeedButtonsSpeedStep", getApplicationContext().getResources() .getString(R.string.prefVolumeSpeedButtonsSpeedStepDefaultValue)); prefTtsWhen = prefs.getString("prefTtsWhen", getResources().getString(R.string.prefTtsWhenDefaultValue)); prefTtsThrottleResponse = prefs.getString("prefTtsThrottleResponse", getResources().getString(R.string.prefTtsThrottleResponseDefaultValue)); prefTtsGamepadTest = prefs.getBoolean("prefTtsGamepadTest", getResources().getBoolean(R.bool.prefTtsGamepadTestDefaultValue)); prefTtsGamepadTestComplete = prefs.getBoolean("prefTtsGamepadTestComplete", getResources().getBoolean(R.bool.prefTtsGamepadTestCompleteDefaultValue)); prefEsuMc2EndStopDirectionChange = prefs.getBoolean("prefEsuMc2EndStopDirectionChange", getResources().getBoolean(R.bool.prefEsuMc2EndStopDirectionChangeDefaultValue)); prefEsuMc2StopButtonShortPress = prefs.getBoolean("prefEsuMc2StopButtonShortPress", getResources().getBoolean(R.bool.prefEsuMc2StopButtonShortPressDefaultValue)); mainapp.numThrottles = mainapp.Numeralise( prefs.getString("NumThrottle", getResources().getString(R.string.NumThrottleDefaulValue))); prefThrottleScreenType = prefs.getString("prefThrottleScreenType", getApplicationContext().getResources().getString(R.string.prefThrottleScreenTypeDefault)); prefSelectiveLeadSound = prefs.getBoolean("SelectiveLeadSound", getResources().getBoolean(R.bool.prefSelectiveLeadSoundDefaultValue)); prefSelectiveLeadSoundF1 = prefs.getBoolean("SelectiveLeadSoundF1", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF1DefaultValue)); prefSelectiveLeadSoundF2 = prefs.getBoolean("SelectiveLeadSoundF2", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF2DefaultValue)); if (isCreate) { prefs.edit().putString("prefKidsTimer", PREF_KIDS_TIMER_NONE).commit(); //reset the preference } getKidsTimerPrefs(); } protected void getDirectionButtonPrefs() { DIRECTION_BUTTON_LEFT_TEXT = getApplicationContext().getResources().getString(R.string.forward); DIRECTION_BUTTON_RIGHT_TEXT = getApplicationContext().getResources().getString(R.string.reverse); speedButtonLeftText = getApplicationContext().getResources().getString(R.string.LeftButton); speedButtonRightText = getApplicationContext().getResources().getString(R.string.RightButton); speedButtonUpText = getApplicationContext().getResources().getString(R.string.UpButton); speedButtonDownText = getApplicationContext().getResources().getString(R.string.DownButton); prefSwapForwardReverseButtons = prefs.getBoolean("prefSwapForwardReverseButtons", getResources().getBoolean(R.bool.prefSwapForwardReverseButtonsDefaultValue)); prefSwapForwardReverseButtonsLongPress = prefs.getBoolean("prefSwapForwardReverseButtonsLongPress", getResources().getBoolean(R.bool.prefSwapForwardReverseButtonsLongPressDefaultValue)); prefLeftDirectionButtons = prefs.getString("prefLeftDirectionButtons", getApplicationContext().getResources().getString(R.string.prefLeftDirectionButtonsDefaultValue)) .trim(); prefRightDirectionButtons = prefs.getString("prefRightDirectionButtons", getApplicationContext().getResources().getString(R.string.prefRightDirectionButtonsDefaultValue)) .trim(); prefGamepadSwapForwardReverseWithScreenButtons = prefs.getBoolean( "prefGamepadSwapForwardReverseWithScreenButtons", getResources().getBoolean(R.bool.prefGamepadSwapForwardReverseWithScreenButtonsDefaultValue)); } private void setDirectionButtonLabels() { String dirLeftText; String dirRightText; for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { FullLeftText[throttleIndex] = DIRECTION_BUTTON_LEFT_TEXT; FullRightText[throttleIndex] = DIRECTION_BUTTON_RIGHT_TEXT; dirLeftIndicationText[throttleIndex] = ""; dirRightIndicationText[throttleIndex] = ""; } if (((prefLeftDirectionButtons.equals(DIRECTION_BUTTON_LEFT_TEXT)) && (prefRightDirectionButtons.equals(DIRECTION_BUTTON_RIGHT_TEXT))) || ((prefLeftDirectionButtons.equals("")) && (prefRightDirectionButtons.equals("")))) { for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { if (directionButtonsAreCurrentlyReversed(throttleIndex)) { FullLeftText[throttleIndex] = DIRECTION_BUTTON_RIGHT_TEXT; FullRightText[throttleIndex] = DIRECTION_BUTTON_LEFT_TEXT; } } } else { dirLeftText = prefLeftDirectionButtons; dirRightText = prefRightDirectionButtons; for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { if (!directionButtonsAreCurrentlyReversed(throttleIndex)) { FullLeftText[throttleIndex] = dirLeftText; dirLeftIndicationText[throttleIndex] = getApplicationContext().getResources() .getString(R.string.loco_direction_left_extra); FullRightText[throttleIndex] = dirRightText; dirRightIndicationText[throttleIndex] = getApplicationContext().getResources() .getString(R.string.loco_direction_right_extra); } else { FullLeftText[throttleIndex] = dirLeftText; dirLeftIndicationText[throttleIndex] = getApplicationContext().getResources() .getString(R.string.loco_direction_right_extra); FullRightText[throttleIndex] = dirRightText; dirRightIndicationText[throttleIndex] = getApplicationContext().getResources() .getString(R.string.loco_direction_left_extra); } } } for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { bFwds[throttleIndex].setText(FullLeftText[throttleIndex]); bRevs[throttleIndex].setText(FullRightText[throttleIndex]); tvLeftDirInds[throttleIndex].setText(dirLeftIndicationText[throttleIndex]); tvRightDirInds[throttleIndex].setText(dirRightIndicationText[throttleIndex]); } } private void reloadWeb() { webView.stopLoading(); load_webview(); // reload } private void initWeb() { // reload from the initial webpage currentUrl = null; reloadWeb(); } // used for the swipe up option and the shake, to show or hide the web view at the bottom of the throttle page private void showHideWebView(String toastMsg) { if (!(keepWebViewLocation.equals(WEB_VIEW_LOCATION_NONE))) { // show/hide the web view if the preference is set if (!webViewIsOn) { webViewLocation = keepWebViewLocation; } else { webViewLocation = WEB_VIEW_LOCATION_NONE; Toast.makeText(getApplicationContext(), toastMsg, Toast.LENGTH_SHORT).show(); } webViewIsOn = !webViewIsOn; this.onResume(); } } private void witRetry(String s) { if (this.hasWindowFocus()) { webView.stopLoading(); Intent in = new Intent().setClass(this, reconnect_status.class); in.putExtra("status", s); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); } } protected void removeLoco(int whichThrottle) { disable_buttons(whichThrottle); // direction and slider set_function_labels_and_listeners_for_view(whichThrottle); set_labels(); } // process WiT speed report // update speed slider if didn't just send a speed update to WiT void speedUpdateWiT(int whichThrottle, int speed) { if (speed < 0) speed = 0; if (!changeTimers[whichThrottle].delayInProg) { sbs[whichThrottle].setProgress(speed); // Now update ESU MCII Knob position if (IS_ESU_MCII) { Log.d("Engine_Driver", "ESU_MCII: Move knob request for WiT speed report"); setEsuThrottleKnobPosition(whichThrottle, speed); } } } SeekBar getThrottleSlider(int whichThrottle) { return sbs[whichThrottle]; } double getDisplayUnitScale(int whichThrottle) { return displayUnitScales[whichThrottle]; } // set speed slider to absolute value on all throttles void speedUpdate(int speed) { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { speedUpdate(throttleIndex, speed); } } // set speed slider to absolute value on one throttle void speedUpdate(int whichThrottle, int speed) { if (speed < 0) speed = 0; getThrottleSlider(whichThrottle).setProgress(speed); } // get the current speed of the throttle int getSpeed(int whichThrottle) { return getThrottleSlider(whichThrottle).getProgress(); } private int getScaleSpeed(int whichThrottle) { Consist con = mainapp.consists[whichThrottle]; int speed = getSpeed(whichThrottle); double speedScale = getDisplayUnitScale(whichThrottle); if (speed < 0) { speed = 0; } // int scaleSpeed = (int) Math.round(speed * speedScale) -1; // return scaleSpeed; return (int) Math.round(speed * speedScale) - 1; } int getMaxSpeed(int whichThrottle) { return getThrottleSlider(whichThrottle).getMax(); } int getMinSpeed(int whichThrottle) { return 0; } // returns true if throttle is set for the maximum speed boolean atMaxSpeed(int whichThrottle) { return (getSpeed(whichThrottle) >= getMaxSpeed(whichThrottle)); } // returns true if throttle is set for the minimum speed boolean atMinSpeed(int whichThrottle) { return (getSpeed(whichThrottle) <= getMinSpeed(whichThrottle)); } // change speed slider by scaled display unit value int speedChange(int whichThrottle, int change) { SeekBar throttle_slider = getThrottleSlider(whichThrottle); double displayUnitScale = getDisplayUnitScale(whichThrottle); int lastSpeed = throttle_slider.getProgress(); int lastScaleSpeed = (int) Math.round(lastSpeed * displayUnitScale); int scaleSpeed = lastScaleSpeed + change; int speed = (int) Math.round(scaleSpeed / displayUnitScale); if (lastScaleSpeed == scaleSpeed) { speed += Math.signum(change); } if (speed < 0) //insure speed is inside bounds speed = 0; if (speed > MAX_SPEED_VAL_WIT) speed = MAX_SPEED_VAL_WIT; throttle_slider.setProgress(speed); return speed; } public void decrementSpeed(int whichThrottle, int from) { switch (from) { case SPEED_COMMAND_FROM_BUTTONS: speedChangeAndNotify(whichThrottle, -prefSpeedButtonsSpeedStep); break; case SPEED_COMMAND_FROM_VOLUME: speedChangeAndNotify(whichThrottle, -prefVolumeSpeedButtonsSpeedStep); break; case SPEED_COMMAND_FROM_GAMEPAD: speedChangeAndNotify(whichThrottle, -prefGamePadSpeedButtonsSpeedStep); break; } } public void incrementSpeed(int whichThrottle, int from) { switch (from) { case SPEED_COMMAND_FROM_BUTTONS: speedChangeAndNotify(whichThrottle, prefSpeedButtonsSpeedStep); break; case SPEED_COMMAND_FROM_VOLUME: speedChangeAndNotify(whichThrottle, prefVolumeSpeedButtonsSpeedStep); break; case SPEED_COMMAND_FROM_GAMEPAD: speedChangeAndNotify(whichThrottle, prefGamePadSpeedButtonsSpeedStep); break; } kidsTimerActions(KIDS_TIMER_STARTED, 0); } // set speed slider and notify server for all throttles void speedUpdateAndNotify(int speed) { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { speedUpdateAndNotify(throttleIndex, speed); } } // set speed slider and notify server for one throttle void speedUpdateAndNotify(int whichThrottle, int speed) { speedUpdateAndNotify(whichThrottle, speed, true); } // set speed slider and notify server for one throttle and optionally move ESU MCII knob void speedUpdateAndNotify(int whichThrottle, int speed, boolean moveMc2Knob) { speedUpdate(whichThrottle, speed); sendSpeedMsg(whichThrottle, speed); // Now update ESU MCII Knob position if (IS_ESU_MCII && moveMc2Knob) { Log.d("Engine_Driver", "ESU_MCII: Move knob request for speed update"); setEsuThrottleKnobPosition(whichThrottle, speed); } } // change speed slider by scaled value and notify server public void speedChangeAndNotify(int whichThrottle, int change) { int speed = speedChange(whichThrottle, change); sendSpeedMsg(whichThrottle, speed); // Now update ESU MCII Knob position if (IS_ESU_MCII) { Log.d("Engine_Driver", "ESU_MCII: Move knob request for speed change"); setEsuThrottleKnobPosition(whichThrottle, speed); } } // set the displayed numeric speed value @SuppressLint("SetTextI18n") protected void setDisplayedSpeed(int whichThrottle, int speed) { TextView speed_label; double speedScale = getDisplayUnitScale(whichThrottle); speed_label = tvSpdVals[whichThrottle]; if (speed < 0) { Toast.makeText( getApplicationContext(), getApplicationContext().getResources() .getString(R.string.toastThrottleAlertEstop, getConsistAddressString(whichThrottle)), Toast.LENGTH_LONG).show(); speed = 0; } int scaleSpeed = (int) Math.round(speed * speedScale); speed_label.setText(Integer.toString(scaleSpeed)); } //adjust maxspeedsteps from code passed from JMRI, but only if set to Auto, else do not change private void setSpeedStepsFromWiT(int whichThrottle, int speedStepCode) { int maxSpeedStep = 100; switch (speedStepCode) { case SPEED_STEP_CODE_128: maxSpeedStep = 126; break; case SPEED_STEP_CODE_27: maxSpeedStep = 27; break; case SPEED_STEP_CODE_14: maxSpeedStep = 14; break; case SPEED_STEP_CODE_28: maxSpeedStep = 28; break; } int zeroTrim = preferences.getIntPrefValue(prefs, "prefEsuMc2ZeroTrim", getApplicationContext().getResources().getString(R.string.prefEsuMc2ZeroTrimDefaultValue)); ThrottleScale esuThrottleScale = new ThrottleScale(zeroTrim, maxSpeedStep + 1); maxSpeedSteps[whichThrottle] = maxSpeedStep; esuThrottleScales[whichThrottle] = esuThrottleScale; setDisplayUnitScale(whichThrottle); } //get max speedstep based on Preferences //unless pref is set to AUTO in which case just return the input value private int getSpeedSteps(int maxStep) { if (speedStepPref != SPEED_STEP_AUTO_MODE) { maxStep = speedStepPref; } return maxStep; } protected void setDisplayUnitScale(int whichThrottle) { int maxStep; maxStep = getSpeedSteps(maxSpeedSteps[whichThrottle]); displayUnitScales[whichThrottle] = calcDisplayUnitScale(maxStep); tvSpdLabs[whichThrottle].setText(calcDisplayUnitLabel(maxStep)); } private double calcDisplayUnitScale(int maxSpeedStep) { return maxSpeedStep / (double) MAX_SPEED_VAL_WIT; } private String calcDisplayUnitLabel(int maxSpeedStep) { if (maxSpeedStep == 100) { return getApplicationContext().getResources().getString(R.string.label_percent); } else { return String.valueOf(maxSpeedStep); } } // set the direction for all engines on the throttle // if skipLead is true, the direction is not set for the lead engine void setEngineDirection(int whichThrottle, int direction, boolean skipLead) { Consist con; con = mainapp.consists[whichThrottle]; dirs[whichThrottle] = direction; String leadAddr = con.getLeadAddr(); for (String addr : con.getList()) { // loop through each engine in consist if (!skipLead || (addr != null && !addr.equals(leadAddr))) { int locoDir = direction; try { if (con.isReverseOfLead(addr)) // if engine faces opposite of lead loco locoDir ^= 1; // then reverse the commanded direction mainapp.sendMsg(mainapp.comm_msg_handler, message_type.DIRECTION, addr, whichThrottle, locoDir); } catch (Exception e) { // isReverseOfLead returns null if addr is not in con - should never happen since we are walking through consist list Log.d("Engine_Driver", "throttle " + mainapp.throttleIntToString(whichThrottle) + " direction change for unselected loco " + addr); } } } } private String getConsistAddressString(int whichThrottle) { String result; if (!prefShowAddressInsteadOfName) { result = mainapp.consists[whichThrottle].toString(); } else { result = mainapp.consists[whichThrottle].formatConsistAddr(); } return result; } // get the consist for the specified throttle private Consist getConsist(int whichThrottle) { return mainapp.consists[whichThrottle]; } // get the indicated direction from the button pressed state private int getDirection(int whichThrottle) { return dirs[whichThrottle]; } // update the direction indicators void showDirectionIndications() { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { showDirectionIndication(throttleIndex, dirs[throttleIndex]); } } // indicate direction using the button pressed state void showDirectionIndication(int whichThrottle, int direction) { boolean setLeftDirectionButtonEnabled; if (direction == 0) { //0=reverse 1=forward setLeftDirectionButtonEnabled = directionButtonsAreCurrentlyReversed(whichThrottle); } else { setLeftDirectionButtonEnabled = !directionButtonsAreCurrentlyReversed(whichThrottle); } if (!getConsist(whichThrottle).isActive()) { bFwds[whichThrottle].setSelected(false); bRevs[whichThrottle].setSelected(false); } else { if (!setLeftDirectionButtonEnabled) { bFwds[whichThrottle].setSelected(false); bRevs[whichThrottle].setSelected(true); bFwds[whichThrottle].setTypeface(null, Typeface.NORMAL); bRevs[whichThrottle].setTypeface(null, Typeface.ITALIC + Typeface.BOLD); if ((getSpeed(whichThrottle) > 0) && (!dirChangeWhileMoving)) { bFwds[whichThrottle].setEnabled(false); } bRevs[whichThrottle].setEnabled(true); } else { bFwds[whichThrottle].setSelected(true); bRevs[whichThrottle].setSelected(false); bFwds[whichThrottle].setTypeface(null, Typeface.ITALIC + Typeface.BOLD); bRevs[whichThrottle].setTypeface(null, Typeface.NORMAL); bFwds[whichThrottle].setEnabled(true); if ((getSpeed(whichThrottle) > 0) && (!dirChangeWhileMoving)) { bRevs[whichThrottle].setEnabled(false); } } } } // indicate requested direction using the button typeface void showDirectionRequest(int whichThrottle, int direction) { showDirectionIndication(whichThrottle, direction); } // // processes a direction change if allowed by related preferences and current speed // returns true if throttle direction matches the requested direction private boolean changeDirectionIfAllowed(int whichThrottle, int direction) { if ((getDirection(whichThrottle) != direction) && isChangeDirectionAllowed(whichThrottle)) { // set speed to zero on direction change while moving if the preference is set if (stopOnDirectionChange && (getSpeed(whichThrottle) != 0)) { speedUpdateAndNotify(whichThrottle, 0); } showDirectionRequest(whichThrottle, direction); // update requested direction indication setEngineDirection(whichThrottle, direction, false); // update direction for each engine on this throttle } return (getDirection(whichThrottle) == direction); } private boolean isChangeDirectionAllowed(int whichThrottle) { // check whether direction change is permitted boolean isAllowed = false; if (getConsist(whichThrottle).isActive()) { if (stopOnDirectionChange || dirChangeWhileMoving || (getSpeed(whichThrottle) == 0)) isAllowed = true; } return isAllowed; } void set_stop_button(int whichThrottle, boolean pressed) { if (pressed) { bStops[whichThrottle].setPressed(true); bStops[whichThrottle].setTypeface(null, Typeface.ITALIC); } else { bStops[whichThrottle].setPressed(false); bStops[whichThrottle].setTypeface(null, Typeface.NORMAL); } } private boolean isSelectLocoAllowed(int whichThrottle) { // check whether loco change is permitted boolean isAllowed = false; if (!getConsist(whichThrottle).isActive() || locoSelectWhileMoving || (getSpeed(whichThrottle) == 0)) { isAllowed = true; } return isAllowed; } void start_select_loco_activity(int whichThrottle) { // give feedback that select button was pressed bSels[whichThrottle].setPressed(true); try { Intent select_loco = new Intent().setClass(this, select_loco.class); select_loco.putExtra("sWhichThrottle", mainapp.throttleIntToString(whichThrottle)); // pass whichThrottle as an extra to activity navigatingAway = true; startActivityForResult(select_loco, ACTIVITY_SELECT_LOCO); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); } catch (Exception ex) { Log.d("Engine_Driver", ex.getMessage()); } } void start_gamepad_test_activity(int gamepadNo) { if (prefGamepadTestEnforceTesting) { gamePadDeviceIdsTested[gamepadNo] = 0; try { Intent in = new Intent().setClass(this, gamepad_test.class); in.putExtra("whichGamepadNo", Integer.toString(gamepadNo)); navigatingAway = true; speakWords(TTS_MSG_GAMEPAD_GAMEPAD_TEST, ' '); startActivityForResult(in, ACTIVITY_GAMEPAD_TEST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); } catch (Exception ex) { Log.d("Engine_Driver", ex.getMessage()); } } else { // don't bother doing the test if the preference is set not to gamePadDeviceIdsTested[gamepadNo] = GAMEPAD_GOOD; } } // Edit the Consist Lights // void start_consist_lights_edit(char whichThrottle) { void start_consist_lights_edit(int whichThrottle) { if (prefConsistLightsLongClick) { // only allow the editing in the consist lights if the preference is set try { Intent consistLightsEdit = new Intent().setClass(this, ConsistLightsEdit.class); consistLightsEdit.putExtra("whichThrottle", mainapp.throttleIntToChar(whichThrottle)); navigatingAway = true; startActivityForResult(consistLightsEdit, throttle.ACTIVITY_CONSIST_LIGHTS); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); } catch (Exception ex) { Log.d("Engine_Driver", ex.getMessage()); } } } void disable_buttons(int whichThrottle) { enable_disable_buttons(whichThrottle, true); } void enable_disable_buttons(int whichThrottle) { enable_disable_buttons(whichThrottle, false); } void applySpeedRelatedOptions() { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { applySpeedRelatedOptions(throttleIndex); } // repeat for all three throttles } void applySpeedRelatedOptions(int whichThrottle) { Button bFwd = bFwds[whichThrottle]; Button bRev = bRevs[whichThrottle]; Button bSel = bSels[whichThrottle]; boolean tIsEnabled = lls[whichThrottle].isEnabled(); int dir = dirs[whichThrottle]; if (getConsist(whichThrottle).isActive()) { boolean dirChangeAllowed = tIsEnabled && isChangeDirectionAllowed(whichThrottle); boolean locoChangeAllowed = tIsEnabled && isSelectLocoAllowed(whichThrottle); if (dirChangeAllowed) { bFwd.setEnabled(true); bRev.setEnabled(true); } else { if (dir == 1) { if (!directionButtonsAreCurrentlyReversed(whichThrottle)) { bFwd.setEnabled(true); bRev.setEnabled(false); } else { bFwd.setEnabled(false); bRev.setEnabled(true); } } else { if (!directionButtonsAreCurrentlyReversed(whichThrottle)) { bFwd.setEnabled(false); bRev.setEnabled(true); } else { bFwd.setEnabled(true); bRev.setEnabled(false); } } } bSel.setEnabled(locoChangeAllowed); } else { bFwd.setEnabled(false); bRev.setEnabled(false); bSel.setEnabled(true); } } void enable_disable_buttons(int whichThrottle, boolean forceDisable) { boolean newEnabledState = false; // avoid index and null crashes if (mainapp.consists == null || whichThrottle >= mainapp.consists.length || bFwds[whichThrottle] == null) { return; } if (!forceDisable) { // avoid index crash, but may simply push to next line newEnabledState = mainapp.consists[whichThrottle].isActive(); // set false if lead loco is not assigned } bFwds[whichThrottle].setEnabled(newEnabledState); bRevs[whichThrottle].setEnabled(newEnabledState); bStops[whichThrottle].setEnabled(newEnabledState); tvSpdLabs[whichThrottle].setEnabled(newEnabledState); tvSpdVals[whichThrottle].setEnabled(newEnabledState); bLSpds[whichThrottle].setEnabled(newEnabledState); bRSpds[whichThrottle].setEnabled(newEnabledState); enable_disable_buttons_for_view(fbs[whichThrottle], newEnabledState); if (!newEnabledState) { sbs[whichThrottle].setProgress(0); // set slider to 0 if disabled } sbs[whichThrottle].setEnabled(newEnabledState); } // end of enable_disable_buttons // helper function to enable/disable all children for a group void enable_disable_buttons_for_view(ViewGroup vg, boolean newEnabledState) { // Log.d("Engine_Driver","starting enable_disable_buttons_for_view " + // implemented in derived class, but called from this class } // update the appearance of all function buttons void set_all_function_states(int whichThrottle) { // Log.d("Engine_Driver","set_function_states"); // implemented in derived class, but called from this class } // update a function button appearance based on its state void set_function_state(int whichThrottle, int function) { // Log.d("Engine_Driver","starting set_function_request"); // implemented in derived class, but called from this class } /* * future use: displays the requested function state independent of (in addition to) feedback state * todo: need to handle momentary buttons somehow */ void set_function_request(int whichThrottle, int function, int reqState) { // Log.d("Engine_Driver","starting set_function_request"); Button b; b = functionMaps[whichThrottle].get(function); if (b != null) { if (reqState != 0) { b.setTypeface(null, Typeface.ITALIC + Typeface.BOLD); } else { b.setTypeface(null, Typeface.NORMAL); } } } private void clearVolumeAndGamepadAdditionalIndicators() { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (throttleIndex < bSels.length) { if ((prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_NONE)) || (prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_GAMEPAD))) { bSels[throttleIndex].setActivated(false); } if ((prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_NONE)) || (prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_VOLUME))) { bSels[throttleIndex].setHovered(false); } } } } @SuppressLint("NewApi") private void setSelectedLocoAdditionalIndicator(int whichThrottle, boolean isVolume) { if (!prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_NONE)) { if (mainapp.androidVersion >= mainapp.minActivatedButtonsVersion) { if ((isVolume) && (((prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_BOTH)) || (prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_VOLUME))))) { // note: 'Volume' option is no longer available if (!prefDisableVolumeKeys) { // don't set the indicators if the volume keys are disabled the preferences for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (whichThrottle == throttleIndex) { bSels[throttleIndex].setActivated(true); } else { bSels[throttleIndex].setActivated(false); } } } } if ((!isVolume) && (((prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_BOTH)) || (prefSelectedLocoIndicator.equals(SELECTED_LOCO_INDICATOR_GAMEPAD))))) { for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (whichThrottle == throttleIndex) { bSels[throttleIndex].setHovered(true); } else { bSels[throttleIndex].setHovered(false); } } } } } } // For TTS private void speakWords(int msgNo, int whichThrottle) { boolean result = false; String speech = ""; if (!prefTtsWhen.equals(PREF_TT_WHEN_NONE)) { if (myTts != null) { switch (msgNo) { case TTS_MSG_VOLUME_THROTTLE: if (!prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_NONE)) { if (whichLastVolume != whichThrottle) { result = true; whichLastVolume = whichThrottle; speech = getApplicationContext().getResources().getString(R.string.TtsVolumeThrottle) + " " + (whichThrottle + 1); } if ((prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO)) || (prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO_SPEED))) { speech = speech + ", " + getApplicationContext().getResources().getString(R.string.TtsLoco) + " " + (getConsistAddressString(whichThrottle)); } if ((prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_SPEED)) || (prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO_SPEED))) { speech = speech + ", " + getApplicationContext().getResources().getString(R.string.TtsSpeed) + " " + (getScaleSpeed(whichThrottle) + 1); } } break; case TTS_MSG_GAMEPAD_THROTTLE: if (!prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_NONE)) { if (whichLastGamepad1 != whichThrottle) { result = true; whichLastGamepad1 = whichThrottle; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadThrottle) + " " + (whichThrottle + 1); } if ((prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO)) || (prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO_SPEED))) { speech = speech + ", " + getApplicationContext().getResources().getString(R.string.TtsLoco) + " " + (getConsistAddressString(whichThrottle)); } if ((prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_SPEED)) || (prefTtsThrottleResponse.equals(PREF_TTS_THROTTLE_RESPONSE_LOCO_SPEED))) { speech = speech + ", " + getApplicationContext().getResources().getString(R.string.TtsSpeed) + " " + (getScaleSpeed(whichThrottle) + 1); } } break; case TTS_MSG_GAMEPAD_GAMEPAD_TEST: if ((prefTtsGamepadTest)) { result = true; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadTest); } break; case TTS_MSG_GAMEPAD_GAMEPAD_TEST_COMPLETE: if ((prefTtsGamepadTestComplete)) { result = true; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadTestComplete); } break; case TTS_MSG_GAMEPAD_GAMEPAD_TEST_SKIPPED: if ((prefTtsGamepadTestComplete)) { result = true; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadTestSkipped); } break; case TTS_MSG_GAMEPAD_GAMEPAD_TEST_FAIL: if ((prefTtsGamepadTestComplete)) { result = true; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadTestFail); } break; case TTS_MSG_GAMEPAD_GAMEPAD_TEST_RESET: if ((prefTtsGamepadTestComplete)) { result = true; speech = getApplicationContext().getResources().getString(R.string.TtsGamepadTestReset); } break; } if (result) { Time currentTime = new Time(); currentTime.setToNow(); // //don't repeat what was last spoken withing 6 seconds if (((currentTime.toMillis(true) >= (lastTtsTime.toMillis(true) + 6000)) || (!speech.equals(lastTts)))) { //myTts.speak(speech, TextToSpeech.QUEUE_FLUSH, null); myTts.speak(speech, TextToSpeech.QUEUE_ADD, null); lastTtsTime = currentTime; } lastTts = speech; } } } } // For TTS private void setupTts() { if (!prefTtsWhen .equals(getApplicationContext().getResources().getString(R.string.prefTtsWhenDefaultValue))) { if (myTts == null) { Intent checkTTSIntent = new Intent(); // checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); // startActivityForResult(checkTTSIntent, MY_TTS_DATA_CHECK_CODE); lastTtsTime = new Time(); lastTtsTime.setToNow(); myTts = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status != TextToSpeech.ERROR) { myTts.setLanguage(Locale.getDefault()); } else { Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastTtsFailed), Toast.LENGTH_SHORT).show(); } } }); } } } // Set the original volume indicator a small 'v' near the speed protected void setVolumeIndicator() { // hide or display volume control indicator based on variable for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { tvVols[throttleIndex].setText(""); if (!prefDisableVolumeKeys) { // don't set the indicators if the volume keys are disabled tvVols[whichVolume].setText(VOLUME_INDICATOR); setSelectedLocoAdditionalIndicator(whichVolume, true); } } // Ensure ESU MCII tracks selected throttle if (IS_ESU_MCII) { Log.d("Engine_Driver", "ESU_MCII: Throttle changed to: " + whichVolume); setEsuThrottleKnobPosition(whichVolume, getSpeed(whichVolume)); if (!isEsuMc2Stopped) { // Set green LED on if controlling a throttle; flash if nothing selected // Only do this if ESU MCII not in Stop mode if (getConsist(whichVolume).isActive()) { esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.ON, true); } else { esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.STEADY_FLASH, true); } } } } protected void setGamepadIndicator() { // hide or display gamepad number indicator based on variable boolean isSet = false; for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (gamePadThrottleAssignment[throttleIndex] != -1) { tvGamePads[throttleIndex].setText(String.valueOf(gamePadThrottleAssignment[throttleIndex])); } else { tvGamePads[throttleIndex].setText(""); } if (gamePadThrottleAssignment[throttleIndex] == 1) { setSelectedLocoAdditionalIndicator(throttleIndex, false); speakWords(TTS_MSG_GAMEPAD_THROTTLE, throttleIndex); isSet = true; } } if (!isSet) { setSelectedLocoAdditionalIndicator(-1, false); } } private void setNextActiveThrottle() { setNextActiveThrottle(false); } private void setNextActiveThrottle(boolean feedbackSound) { int i; int index = -1; // find current Volume throttle for (i = 0; i < mainapp.numThrottles; i++) { if (i == whichVolume) { index = i; break; } } // find next active throttle i = index + 1; while (i != index) { // check until we get back to current Volume throttle if (i >= mainapp.numThrottles) { // wrap i = 0; } else { int whichT = i; if (getConsist(whichT).isActive()) { // found next active throttle whichVolume = whichT; setVolumeIndicator(); break; // done } else { // move to next throttle i++; } } } // play appropriate feedbackSound if (feedbackSound) { GamepadFeedbackSound(i == index); } } // if the preference is set, set the specific active throttle for the volume keys based on the throttle used private void setActiveThrottle(int whichThrottle) { if (tvVols[0] != null) { // if it is null assume that the page is not fully drawn yet if (prefVolumeKeysFollowLastTouchedThrottle) { if (whichVolume != whichThrottle) { whichVolume = whichThrottle; setVolumeIndicator(); setSelectedLocoAdditionalIndicator(whichThrottle, true); } } } } // setup the appropriate keycodes for the type of gamepad that has been selected in the preferences private void setGamepadKeys() { whichGamePadMode = prefs.getString("prefGamePadType", getApplicationContext().getResources().getString(R.string.prefGamePadTypeDefaultValue)); // Gamepad button Preferences prefGamePadButtons[0] = prefs.getString("prefGamePadButtonStart", getApplicationContext().getResources().getString(R.string.prefGamePadButtonStartDefaultValue)); prefGamePadButtons[9] = prefs.getString("prefGamePadButtonReturn", getApplicationContext().getResources().getString(R.string.prefGamePadButtonReturnDefaultValue)); prefGamePadButtons[1] = prefs.getString("prefGamePadButton1", getApplicationContext().getResources().getString(R.string.prefGamePadButton1DefaultValue)); prefGamePadButtons[2] = prefs.getString("prefGamePadButton2", getApplicationContext().getResources().getString(R.string.prefGamePadButton2DefaultValue)); prefGamePadButtons[3] = prefs.getString("prefGamePadButton3", getApplicationContext().getResources().getString(R.string.prefGamePadButton3DefaultValue)); prefGamePadButtons[4] = prefs.getString("prefGamePadButton4", getApplicationContext().getResources().getString(R.string.prefGamePadButton4DefaultValue)); // Gamepad DPAD Preferences prefGamePadButtons[5] = prefs.getString("prefGamePadButtonUp", getApplicationContext().getResources().getString(R.string.prefGamePadButtonUpDefaultValue)); prefGamePadButtons[6] = prefs.getString("prefGamePadButtonRight", getApplicationContext().getResources().getString(R.string.prefGamePadButtonRightDefaultValue)); prefGamePadButtons[7] = prefs.getString("prefGamePadButtonDown", getApplicationContext().getResources().getString(R.string.prefGamePadButtonDownDefaultValue)); prefGamePadButtons[8] = prefs.getString("prefGamePadButtonLeft", getApplicationContext().getResources().getString(R.string.prefGamePadButtonLeftDefaultValue)); //extra buttons prefGamePadButtons[10] = prefs.getString("prefGamePadButtonLeftShoulder", getApplicationContext() .getResources().getString(R.string.prefGamePadButtonLeftTriggerDefaultValue)); prefGamePadButtons[11] = prefs.getString("prefGamePadButtonRightShoulder", getApplicationContext() .getResources().getString(R.string.prefGamePadButtonRightShoulderDefaultValue)); prefGamePadButtons[12] = prefs.getString("prefGamePadButtonLeftTrigger", getApplicationContext() .getResources().getString(R.string.prefGamePadButtonLeftTriggerDefaultValue)); prefGamePadButtons[13] = prefs.getString("prefGamePadButtonRightTrigger", getApplicationContext() .getResources().getString(R.string.prefGamePadButtonRightTriggerDefaultValue)); if (!whichGamePadMode.equals(WHICH_GAMEPAD_MODE_NONE)) { // make sure the Softkeyboard is hidden getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); } int[] bGamePadKeys; int[] bGamePadKeysUp; switch (whichGamePadMode) { case "iCade+DPAD": case "iCade+DPAD-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadiCadePlusDpad); bGamePadKeysUp = this.getResources().getIntArray(R.array.prefGamePadiCadePlusDpad_UpCodes); break; case "MTK": case "MTK-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadMTK); bGamePadKeysUp = bGamePadKeys; break; case "Game": case "Game-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadGame); bGamePadKeysUp = bGamePadKeys; break; /* case "VRBoxA": case "VRBoxA-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadVRBoxA); bGamePadKeysUp = bGamePadKeys; break; case "VRBoxC": case "VRBoxC-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadVRBoxiC); bGamePadKeysUp = this.getResources().getIntArray(R.array.prefGamePadVRBoxiC_UpCodes); break; case "VRBoxiC": case "VRBoxiC-rotate": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadVRBoxiC); bGamePadKeysUp = this.getResources().getIntArray(R.array.prefGamePadVRBoxiC_UpCodes); break; */ case "MagicseeR1B": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadMagicseeR1B); bGamePadKeysUp = bGamePadKeys; break; case "FlydigiWee2": bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadFlydigiWee2); bGamePadKeysUp = bGamePadKeys; break; default: // "iCade" or iCade-rotate bGamePadKeys = this.getResources().getIntArray(R.array.prefGamePadiCade); bGamePadKeysUp = this.getResources().getIntArray(R.array.prefGamePadiCade_UpCodes); break; } // now grab the keycodes and put them into the arrays that will actually be used. for (int i = 0; i <= 14; i++) { gamePadKeys[i] = bGamePadKeys[i]; gamePadKeys_Up[i] = bGamePadKeysUp[i]; } // if the preference name has "-rotate" at the end of it swap the dpad buttons around if (whichGamePadMode.contains("-rotate")) { gamePadKeys[2] = bGamePadKeys[4]; gamePadKeys[3] = bGamePadKeys[5]; gamePadKeys[4] = bGamePadKeys[3]; gamePadKeys[5] = bGamePadKeys[2]; gamePadKeys_Up[2] = bGamePadKeysUp[4]; gamePadKeys_Up[3] = bGamePadKeysUp[5]; gamePadKeys_Up[4] = bGamePadKeysUp[3]; gamePadKeys_Up[5] = bGamePadKeysUp[2]; } } // private int swapToNextAvilableThrottleForGamePad(int fromThrottle, boolean quiet) { int whichThrottle = -1; int index; index = fromThrottle - 1; // need to count through all the throttle, but need to start from the current one, not zero for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { index++; if (index >= mainapp.numThrottles) { index = 0; } if (gamePadIds[index] == 0) { // unassigned if (getConsist(index).isActive()) { // found next active throttle if (gamePadIds[index] <= 0) { //not currently assigned whichThrottle = index; break; // done } } } } if (whichThrottle >= 0) { gamePadIds[whichThrottle] = gamePadIds[fromThrottle]; // gamePadDeviceIdsTested[whichThrottle] = gamePadDeviceIdsTested[fromThrottle]; gamePadThrottleAssignment[whichThrottle] = gamePadThrottleAssignment[fromThrottle]; gamePadIds[fromThrottle] = 0; // gamePadDeviceIdsTested[fromThrottle] = -1; gamePadThrottleAssignment[fromThrottle] = -1; setGamepadIndicator(); } if (whichThrottle == -1) { if (!quiet) { GamepadFeedbackSound(true); } return fromThrottle; // didn't work. leave it alone } else { if (!quiet) { GamepadFeedbackSound(false); } return whichThrottle; } } // work out a) if we need to look for multiple gamepads b) workout which gamepad we received the key event from private int findWhichGamePadEventIsFrom(int eventDeviceId, int eventKeyCode) { int whichGamePad = -2; // default to the event not from a gamepad int whichGamePadDeviceId = -1; int j; // if ((eventKeyCode!=KEYCODE_VOLUME_UP)&&(eventKeyCode!=KEYCODE_VOLUME_DOWN)&&(eventKeyCode!=KEYCODE_BACK)) { // if it is a volume key or the back assume it did not come form a gamepad if (eventDeviceId >= 0) { // event is from a gamepad (or at least not from a screen touch) whichGamePad = -1; // gamepad int reassigningGamepad = -1; int i; // find out if this gamepad is already assigned for (i = 0; i < mainapp.numThrottles; i++) { if (gamePadIds[i] == eventDeviceId) { if (getConsist(i).isActive()) { //found the throttle and it is active whichGamePad = i; } else { // currently assigned to this throttle, but the throttle is not active whichGamePad = i; gamePadIds[i] = 0; reassigningGamepad = gamePadThrottleAssignment[i]; gamePadThrottleAssignment[i] = -1; setGamepadIndicator(); // need to clear the indicator } break; } } if (whichGamePad == -1) { //didn't find it OR is known, but unassigned for (j = 0; j < gamepadCount; j++) { if (gamePadDeviceIds[j] == eventDeviceId) { // known, but unassigned whichGamePadDeviceId = j; break; } } if (whichGamePadDeviceId == -1) { // previously unseen gamepad gamepadCount++; gamePadDeviceIds[gamepadCount - 1] = eventDeviceId; whichGamePadDeviceId = gamepadCount - 1; mainapp.setGamepadTestMenuOption(TMenu, gamepadCount); start_gamepad_test_activity(gamepadCount - 1); } for (i = 0; i < mainapp.numThrottles; i++) { if (gamePadIds[i] == 0) { // throttle is not assigned a gamepad if (getConsist(i).isActive()) { // found next active throttle gamePadIds[i] = eventDeviceId; if (reassigningGamepad == -1) { // not a reassignment gamePadThrottleAssignment[i] = GAMEPAD_INDICATOR[whichGamePadDeviceId]; } else { // reasigning gamePadThrottleAssignment[i] = reassigningGamepad; } whichGamePad = i; setGamepadIndicator(); break; // done } } } } else { if (gamePadDeviceIdsTested[i] == GAMEPAD_BAD) { // gamepad is known but failed the test last time start_gamepad_test_activity(i); } } } if (gamepadCount > 0) { usingMultiplePads = true; } // } return whichGamePad; } // map the button pressed to the user selected action for that button on the gamepad private void performButtonAction(int buttonNo, int action, boolean isActive, int whichThrottle, int whichGamePadIsEventFrom, int repeatCnt) { String x = prefGamePadButtons[buttonNo]; if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_ALL_STOP)) { // All Stop if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { GamepadFeedbackSound(false); speedUpdateAndNotify(0); // update all three throttles } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_STOP)) { // Stop if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { GamepadFeedbackSound(false); speedUpdateAndNotify(whichThrottle, 0); } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_NEXT_THROTTLE)) { // Next Throttle if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { if (usingMultiplePads && whichGamePadIsEventFrom >= 0) { swapToNextAvilableThrottleForGamePad(whichGamePadIsEventFrom, false); } else { setNextActiveThrottle(true); } } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_FORWARD)) { // Forward if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { boolean dirChangeFailed; if (!gamepadDirectionButtonsAreCurrentlyReversed(whichThrottle)) { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_FORWARD); } else { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_REVERSE); } GamepadFeedbackSound(dirChangeFailed); } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_REVERSE)) { // Reverse boolean dirChangeFailed; if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { if (!gamepadDirectionButtonsAreCurrentlyReversed(whichThrottle)) { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_REVERSE); } else { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_FORWARD); } GamepadFeedbackSound(dirChangeFailed); } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_FORWARD_REVERSE_TOGGLE)) { // Toggle Forward/Reverse if (isActive && (action == ACTION_DOWN) && (repeatCnt == 0)) { boolean dirChangeFailed; if ((getDirection(whichThrottle) == DIRECTION_FORWARD)) { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_REVERSE); } else { dirChangeFailed = !changeDirectionIfAllowed(whichThrottle, DIRECTION_FORWARD); } GamepadFeedbackSound(dirChangeFailed); } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_INCREASE_SPEED)) { // Increase Speed if (isActive && (action == ACTION_DOWN)) { if (repeatCnt == 0) { /* GamepadIncrementSpeed(whichThrottle); } // if longpress, start repeater else if (repeatCnt == 1) { */ mGamepadAutoIncrement = true; gamepadRepeatUpdateHandler.post(new GamepadRptUpdater(whichThrottle)); } } } else if (prefGamePadButtons[buttonNo].equals(PREF_GAMEPAD_BUTTON_OPTION_DECREASE_SPEED)) { // Decrease Speed if (isActive && (action == ACTION_DOWN)) { if (repeatCnt == 0) { /* GamepadDecrementSpeed(whichThrottle); } // if longpress, start repeater else if (repeatCnt == 1) { */ mGamepadAutoDecrement = true; gamepadRepeatUpdateHandler.post(new GamepadRptUpdater(whichThrottle)); } } } else if ((prefGamePadButtons[buttonNo].length() >= 11) && (prefGamePadButtons[buttonNo].substring(0, 9).equals(GAMEPAD_FUNCTION_PREFIX))) { // one of the Function Buttons int fKey = Integer.parseInt(prefGamePadButtons[buttonNo].substring(9, 11)); if (isActive && (repeatCnt == 0)) { String lab = mainapp.function_labels[whichThrottle].get(fKey); if (lab != null) { lab = lab.toUpperCase().trim(); } else { lab = mainapp.function_labels_default.get(fKey).toUpperCase().trim(); } boolean leadOnly = false; boolean trailOnly = false; boolean followLeadFunction = false; int result = isFunctionLeadTrailFollow(whichThrottle, fKey, lab); if ((result == FUNCTION_IS_LEAD_ONLY) || (result == FUNCTION_IS_LEAD_AND_FOLLOW) || (result == FUNCTION_IS_LEAD_AND_TRAIL)) { leadOnly = true; } if ((result == FUNCTION_IS_TRAIL_ONLY) || (result == FUNCTION_IS_LEAD_AND_TRAIL)) { trailOnly = true; } if ((result == FUNCTION_IS_FOLLOW) || (result == FUNCTION_IS_LEAD_AND_FOLLOW)) { followLeadFunction = true; } if (action == ACTION_DOWN) { GamepadFeedbackSound(false); // mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle), fKey, 1); sendFunctionToConsistLocos(whichThrottle, fKey, lab, BUTTON_PRESS_MESSAGE_DOWN, leadOnly, trailOnly, followLeadFunction); } else { // mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle), fKey, 0); sendFunctionToConsistLocos(whichThrottle, fKey, lab, BUTTON_PRESS_MESSAGE_UP, leadOnly, trailOnly, followLeadFunction); } } } } private int getGamePadIndexFromThrottleNo(int whichThrottle) { int whichGamepad = -1; for (int i = 0; i < mainapp.numThrottles; i++) { if (gamePadIds[whichThrottle] == gamePadDeviceIds[i]) { whichGamepad = i; break; } } return whichGamepad; } // listener for the joystick events @Override public boolean dispatchGenericMotionEvent(android.view.MotionEvent event) { //Log.d("Engine_Driver", "dgme " + event.getAction()); // if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { if (!whichGamePadMode.equals(WHICH_GAMEPAD_MODE_NONE)) { // respond to the gamepad and keyboard inputs only if the preference is set boolean acceptEvent = true; // default to assuming that we will respond to the event int action; int whichThrottle; int repeatCnt = 0; int whichGamePadIsEventFrom = findWhichGamePadEventIsFrom(event.getDeviceId(), 0); // dummy eventKeyCode if ((whichGamePadIsEventFrom > -1) && (whichGamePadIsEventFrom < gamePadDeviceIdsTested.length)) { // the event came from a gamepad if (gamePadDeviceIdsTested[getGamePadIndexFromThrottleNo( whichGamePadIsEventFrom)] != GAMEPAD_GOOD) { //if not, testing for this gamepad is not complete or has failed acceptEvent = false; } } else { acceptEvent = false; } if (acceptEvent) { float xAxis; xAxis = event.getAxisValue(MotionEvent.AXIS_X); float yAxis = event.getAxisValue(MotionEvent.AXIS_Y); float xAxis2 = event.getAxisValue(MotionEvent.AXIS_Z); float yAxis2 = event.getAxisValue(MotionEvent.AXIS_RZ); if ((xAxis != 0) || (yAxis != 0)) { action = ACTION_DOWN; } else { action = ACTION_UP; } if ((usingMultiplePads) && (whichGamePadIsEventFrom >= -1)) { // we have multiple gamepads AND the preference is set to make use of them AND the event came for a gamepad if (whichGamePadIsEventFrom >= 0) { whichThrottle = whichGamePadIsEventFrom; } else { GamepadFeedbackSound(true); return (true); } } else { whichThrottle = whichVolume; // work out which throttle the volume keys are currently set to contol... and use that one } boolean isActive = getConsist(whichThrottle).isActive(); if (action == ACTION_UP) { mGamepadAutoIncrement = false; mGamepadAutoDecrement = false; GamepadFeedbackSoundStop(); } if (yAxis == -1) { // DPAD Up Button performButtonAction(5, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (yAxis == 1) { // DPAD Down Button performButtonAction(7, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (xAxis == -1) { // DPAD Left Button performButtonAction(8, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (xAxis == 1) { // DPAD Right Button performButtonAction(6, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } if (yAxis2 == -1) { // DPAD2 Up Button performButtonAction(5, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (yAxis2 == 1) { // DPAD2 Down Button performButtonAction(7, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (xAxis2 == -1) { // DPAD2 Left Button performButtonAction(8, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (xAxis2 == 1) { // DPAD2 Right Button performButtonAction(6, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } } else { // event is from a gamepad that has not finished testing. Ignore it return (true); // stop processing this key } // } } return super.dispatchGenericMotionEvent(event); } // listener for physical keyboard events // used to support the gamepad only DPAD and key events @Override public boolean dispatchKeyEvent(KeyEvent event) { boolean isExternal = false; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { InputDevice idev = getDevice(event.getDeviceId()); if (idev != null && idev.toString() != null && idev.toString().contains("Location: external")) isExternal = true; } if (isExternal) { // if has come from the phone itself, don't try to process it here if (!whichGamePadMode.equals(WHICH_GAMEPAD_MODE_NONE)) { // respond to the gamepad and keyboard inputs only if the preference is set boolean acceptEvent = true; // default to assuming that we will respond to the event int action = event.getAction(); int keyCode = event.getKeyCode(); int repeatCnt = event.getRepeatCount(); int whichThrottle; int whichGamePadIsEventFrom = findWhichGamePadEventIsFrom(event.getDeviceId(), event.getKeyCode()); if ((whichGamePadIsEventFrom > -1) && (whichGamePadIsEventFrom < gamePadDeviceIdsTested.length)) { // the event came from a gamepad if (gamePadDeviceIdsTested[getGamePadIndexFromThrottleNo( whichGamePadIsEventFrom)] != GAMEPAD_GOOD) { //if not, testing for this gamepad is not complete or has failed acceptEvent = false; } } else { acceptEvent = false; } if (acceptEvent) { if ((usingMultiplePads) && (whichGamePadIsEventFrom >= -1)) { // we have multiple gamepads AND the preference is set to make use of them AND the event came for a gamepad if (whichGamePadIsEventFrom >= 0) { whichThrottle = whichGamePadIsEventFrom; } else { GamepadFeedbackSound(true); return (true); } } else { whichThrottle = whichVolume; // work out which throttle the volume keys are currently set to contol... and use that one } boolean isActive = getConsist(whichThrottle).isActive(); if (keyCode != 0) { Log.d("Engine_Driver", "keycode " + keyCode + " action " + action + " repeat " + repeatCnt); } if (action == ACTION_UP) { mGamepadAutoIncrement = false; mGamepadAutoDecrement = false; GamepadFeedbackSoundStop(); } if (keyCode == gamePadKeys[2]) { // DPAD Up Button performButtonAction(5, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[3]) { // DPAD Down Button performButtonAction(7, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[4]) { // DPAD Left Button performButtonAction(8, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[5]) { // DPAD Right Button performButtonAction(6, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[7]) { // ios button performButtonAction(1, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys_Up[8]) { // X button performButtonAction(3, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys_Up[9]) { // Triangle button performButtonAction(2, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys_Up[10]) { // @ button performButtonAction(4, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[6]) { // start button performButtonAction(0, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[11]) { // Left Shoulder button performButtonAction(10, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[12]) { // Left Shoulder button performButtonAction(11, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[13]) { // Left Shoulder button performButtonAction(12, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[14]) { // Left Shoulder button performButtonAction(13, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } else if (keyCode == gamePadKeys[1]) { // Return button // NextThrottle /* if ((action == ACTION_DOWN) && (repeatCnt == 0)) { if (usingMultiplePads && whichGamePadIsEventFrom >= 0) { whichGamePadIsEventFrom = swapToNextAvilableThrottleForGamePad(whichGamePadIsEventFrom, false); } else { setNextActiveThrottle(true); } } */ performButtonAction(9, action, isActive, whichThrottle, whichGamePadIsEventFrom, repeatCnt); return (true); // stop processing this key } } else { // event is from a gamepad that has not finished testing. Ignore it return (true); // stop processing this key } // for now pass all keystrokes not in gamePadKeys[] to super // if problems occur, we can uncomment the next 2 lines // else if (!((keyCode == KEYCODE_BACK) || (keyCode == KEYCODE_VOLUME_DOWN) || (keyCode == KEYCODE_VOLUME_UP) || (keyCode == KEYCODE_MENU))) // return (true); // swallow all other keystrokes except back, menu and the volume keys } } else if (IS_ESU_MCII) { // Process ESU MCII keys int action = event.getAction(); int keyCode = event.getKeyCode(); int repeatCnt = event.getRepeatCount(); boolean isActive = getConsist(whichVolume).isActive(); if (keyCode != 0) { Log.d("Engine_Driver", "ESU_MCII: keycode " + keyCode + " action " + action + " repeat " + repeatCnt); if (action == ACTION_UP) { esuButtonAutoIncrement = false; esuButtonAutoDecrement = false; } switch (keyCode) { case MobileControl2.KEYCODE_TOP_LEFT: case MobileControl2.KEYCODE_BOTTOM_LEFT: case MobileControl2.KEYCODE_TOP_RIGHT: case MobileControl2.KEYCODE_BOTTOM_RIGHT: performEsuMc2ButtonAction(keyCode, action, isActive, whichVolume, repeatCnt); return true; // stop processing this key default: // Unrecognised key - do nothing Log.d("Engine_Driver", "ESU_MCII: Unrecognised keyCode: " + keyCode); } } } return super.dispatchKeyEvent(event); } void GamepadIncrementSpeed(int whichThrottle) { incrementSpeed(whichThrottle, SPEED_COMMAND_FROM_GAMEPAD); GamepadFeedbackSound(atMaxSpeed(whichThrottle)); } void GamepadDecrementSpeed(int whichThrottle) { decrementSpeed(whichThrottle, SPEED_COMMAND_FROM_GAMEPAD); GamepadFeedbackSound(atMinSpeed(whichThrottle)); } void GamepadFeedbackSound(boolean invalidAction) { if (mainapp.appIsFinishing) { return; } if (tg != null) { if (invalidAction) tg.startTone(ToneGenerator.TONE_PROP_NACK); else tg.startTone(ToneGenerator.TONE_PROP_BEEP); } } void GamepadFeedbackSoundStop() { if (tg != null) { tg.stopTone(); } } // For gamepad speed buttons. private class GamepadRptUpdater implements Runnable { int whichThrottle; private GamepadRptUpdater(int WhichThrottle) { whichThrottle = WhichThrottle; try { //REP_DELAY = Integer.parseInt(prefs.getString("speed_arrows_throttle_repeat_delay", "100")); REP_DELAY = Integer.parseInt( prefs.getString("prefGamePadSpeedArrowsThrottleRepeatDelay", getApplicationContext() .getResources().getString(R.string.prefGamePadSpeedButtonsRepeatDefaultValue))); } catch (NumberFormatException ex) { REP_DELAY = 300; } } @Override public void run() { if (mainapp.appIsFinishing) { return; } if (mGamepadAutoIncrement) { GamepadIncrementSpeed(whichThrottle); gamepadRepeatUpdateHandler.postDelayed(new GamepadRptUpdater(whichThrottle), REP_DELAY); } else if (mGamepadAutoDecrement) { GamepadDecrementSpeed(whichThrottle); gamepadRepeatUpdateHandler.postDelayed(new GamepadRptUpdater(whichThrottle), REP_DELAY); } } } // For volume speed buttons. private class volumeKeysRptUpdater implements Runnable { int whichThrottle; private volumeKeysRptUpdater(int WhichThrottle) { whichThrottle = WhichThrottle; try { REP_DELAY = Integer.parseInt(prefs.getString("speed_arrows_throttle_repeat_delay", "100")); } catch (NumberFormatException ex) { REP_DELAY = 100; } } @Override public void run() { if (mVolumeKeysAutoIncrement) { incrementSpeed(whichThrottle, SPEED_COMMAND_FROM_VOLUME); volumeKeysRepeatUpdateHandler.postDelayed(new volumeKeysRptUpdater(whichThrottle), REP_DELAY); } else if (mVolumeKeysAutoDecrement) { decrementSpeed(whichThrottle, SPEED_COMMAND_FROM_VOLUME); volumeKeysRepeatUpdateHandler.postDelayed(new volumeKeysRptUpdater(whichThrottle), REP_DELAY); } } } /** * For ESU MCII buttons. * * However, it seems that the ESU MCII buttons report UP immediately even if held down * so this, right now, is rather superfluous... */ private class EsuMc2ButtonRptUpdater implements Runnable { // char whichThrottle; int whichThrottle; // private EsuMc2ButtonRptUpdater(char whichThrottle) { private EsuMc2ButtonRptUpdater(int whichThrottle) { this.whichThrottle = whichThrottle; try { REP_DELAY = Integer.parseInt(prefs.getString("prefEsuMc2ButtonsRepeatDelay", getApplicationContext() .getResources().getString(R.string.prefEsuMc2ButtonsRepeatDefaultValue))); } catch (NumberFormatException ex) { REP_DELAY = 300; } } @Override public void run() { if (esuButtonAutoIncrement) { incrementSpeed(whichThrottle, SPEED_COMMAND_FROM_VOLUME); esuButtonRepeatUpdateHandler.postDelayed(new EsuMc2ButtonRptUpdater(whichThrottle), REP_DELAY); } else if (esuButtonAutoDecrement) { decrementSpeed(whichThrottle, SPEED_COMMAND_FROM_VOLUME); gamepadRepeatUpdateHandler.postDelayed(new EsuMc2ButtonRptUpdater(whichThrottle), REP_DELAY); } } } // Callback for ESU MCII throttle knob events private ThrottleFragment.OnThrottleListener esuOnThrottleListener = new ThrottleFragment.OnThrottleListener() { @Override public void onButtonDown() { Log.d("Engine_Driver", "ESU_MCII: Knob button down for throttle " + whichVolume); if (!isScreenLocked) { if (!isEsuMc2KnobEnabled) { Log.d("Engine_Driver", "ESU_MCII: Knob disabled - direction change ignored"); } else if (prefEsuMc2EndStopDirectionChange) { Log.d("Engine_Driver", "ESU_MCII: Attempting to switch direction"); changeDirectionIfAllowed(whichVolume, (getDirection(whichVolume) == 1 ? 0 : 1)); speedUpdateAndNotify(whichVolume, 0, false); } else { Log.d("Engine_Driver", "ESU_MCII: Direction change option disabled - do nothing"); speedUpdateAndNotify(whichVolume, 0, false); } } else { Log.d("Engine_Driver", "ESU_MCII: Screen locked - do nothing"); } } @Override public void onButtonUp() { Log.d("Engine_Driver", "ESU_MCII: Knob button up for throttle " + whichVolume); } @Override public void onPositionChanged(int knobPos) { int speed; if (!isScreenLocked) { if (!isEsuMc2KnobEnabled) { Log.d("Engine_Driver", "ESU_MCII: Disabled knob position moved for throttle " + whichVolume); Log.d("Engine_Driver", "ESU_MCII: Nothing updated"); } else if (getConsist(whichVolume).isActive() && !isEsuMc2Stopped) { speed = esuThrottleScales[whichVolume].positionToStep(knobPos); Log.d("Engine_Driver", "ESU_MCII: Knob position changed for throttle " + whichVolume); Log.d("Engine_Driver", "ESU_MCII: New knob position: " + knobPos + " ; speedstep: " + speed); speedUpdateAndNotify(whichVolume, speed, false); // No need to move knob } else { // Ignore knob movements for stopped or inactive throttles Log.d("Engine_Driver", "ESU_MCII: Knob position moved for " + (isEsuMc2Stopped ? "stopped" : "inactive") + " throttle " + whichVolume); Log.d("Engine_Driver", "ESU_MCII: Nothing updated"); } } else { Log.d("Engine_Driver", "ESU_MCII: Screen locked - do nothing"); } } }; // Callback for ESU MCII stop button private StopButtonFragment.OnStopButtonListener esuOnStopButtonListener = new StopButtonFragment.OnStopButtonListener() { private int origSpeed; private long timePressed; private boolean wasLongPress = false; private int delay; private CountDownTimer buttonTimer; @Override public void onStopButtonDown() { if (!getConsist(whichVolume).isActive()) { Log.d("Engine_Driver", "ESU_MCII: Stop button down for inactive throttle " + whichVolume); return; } Log.d("Engine_Driver", "ESU_MCII: Stop button down for throttle " + whichVolume); if (!isEsuMc2Stopped) { origSpeed = getSpeed(whichVolume); timePressed = System.currentTimeMillis(); Log.d("Engine_Driver", "ESU_MCII: Speed value was: " + origSpeed); } // Toggle press status isEsuMc2Stopped = !isEsuMc2Stopped; if (prefEsuMc2StopButtonShortPress) { set_stop_button(whichVolume, true); speedUpdateAndNotify(whichVolume, 0); } esuMc2Led.setState(EsuMc2Led.RED, EsuMc2LedState.ON); esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.OFF); // Read current stop button delay pref value delay = preferences.getIntPrefValue(prefs, "prefEsuMc2StopButtonDelay", getApplicationContext() .getResources().getString(R.string.prefEsuMc2StopButtonDelayDefaultValue)); buttonTimer = new CountDownTimer(delay, delay) { @Override public void onTick(long l) { // do nothing... } @Override public void onFinish() { doStopButtonUp(true); } }; buttonTimer.start(); } @Override public void onStopButtonUp() { if (buttonTimer != null) { buttonTimer.cancel(); } doStopButtonUp(false); } private void doStopButtonUp(boolean fromTimer) { if (buttonTimer != null) { buttonTimer.cancel(); } if (!getConsist(whichVolume).isActive()) { Log.d("Engine_Driver", "ESU_MCII: Stop button up for inactive throttle " + whichVolume); return; } if (fromTimer) { Log.d("Engine_Driver", "ESU_MCII: Stop button timer finished for throttle " + whichVolume); } else { Log.d("Engine_Driver", "ESU_MCII: Stop button up for throttle " + whichVolume); } if (isEsuMc2Stopped) { if (fromTimer || System.currentTimeMillis() - timePressed > delay) { // It's a long initial press so record this wasLongPress = true; Log.d("Engine_Driver", "ESU_MCII: Stop button press was long - long flash Red LED"); esuMc2Led.setState(EsuMc2Led.RED, EsuMc2LedState.LONG_FLASH); esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.OFF); // Set all throttles to zero for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { set_stop_button(throttleIndex, true); speedUpdateAndNotify(throttleIndex, 0); setEnabledEsuMc2ThrottleScreenButtons(throttleIndex, false); } isEsuMc2AllStopped = true; } else { wasLongPress = false; if (prefEsuMc2StopButtonShortPress) { Log.d("Engine_Driver", "ESU_MCII: Stop button press was short - short flash Red LED"); esuMc2Led.setState(EsuMc2Led.RED, EsuMc2LedState.QUICK_FLASH); esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.OFF); setEnabledEsuMc2ThrottleScreenButtons(whichVolume, false); } else { Log.d("Engine_Driver", "ESU_MCII: Stop button press was short but action disabled"); isEsuMc2Stopped = !isEsuMc2Stopped; esuMc2Led.revertLEDStates(); } } } else { if (!wasLongPress) { if (prefEsuMc2StopButtonShortPress) { Log.d("Engine_Driver", "ESU_MCII: Revert speed value to: " + origSpeed); set_stop_button(whichVolume, false); speedUpdateAndNotify(whichVolume, origSpeed); setEnabledEsuMc2ThrottleScreenButtons(whichVolume, true); } else { Log.d("Engine_Driver", "ESU_MCII: Stop button press was short but revert action disabled"); } } else { Log.d("Engine_Driver", "ESU_MCII: Resume control without speed revert"); origSpeed = 0; for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { set_stop_button(throttleIndex, false); setEnabledEsuMc2ThrottleScreenButtons(throttleIndex, true); } isEsuMc2AllStopped = false; } // Revert LED states esuMc2Led.revertLEDStates(); } } }; // Function to move ESU MCII control knob // private void setEsuThrottleKnobPosition(char whichThrottle, int speed) { private void setEsuThrottleKnobPosition(int whichThrottle, int speed) { Log.d("Engine_Driver", "ESU_MCII: Request to update knob position for throttle " + whichThrottle); if (whichThrottle == whichVolume) { int knobPos; knobPos = esuThrottleScales[whichThrottle].stepToPosition(speed); Log.d("Engine_Driver", "ESU_MCII: Update knob position for throttle " + mainapp.throttleIntToString(whichThrottle)); Log.d("Engine_Driver", "ESU_MCII: New knob position: " + knobPos + " ; speedstep: " + speed); esuThrottleFragment.moveThrottle(knobPos); } else { Log.d("Engine_Driver", "ESU_MCII: This throttle not selected for control by knob"); } } private void updateEsuMc2ZeroTrim() { int zeroTrim = preferences.getIntPrefValue(prefs, "prefEsuMc2ZeroTrim", getApplicationContext().getResources().getString(R.string.prefEsuMc2ZeroTrimDefaultValue)); Log.d("Engine_Driver", "ESU_MCII: Update zero trim for throttle to: " + zeroTrim); // first the knob esuThrottleFragment.setZeroPosition(zeroTrim); // now throttle scales for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { esuThrottleScales[throttleIndex] = new ThrottleScale(zeroTrim, esuThrottleScales[throttleIndex].getStepCount()); } } // private void performEsuMc2ButtonAction(int buttonNo, int action, boolean isActive, char whichThrottle, int repeatCnt) { private void performEsuMc2ButtonAction(int buttonNo, int action, boolean isActive, int whichThrottle, int repeatCnt) { if (isEsuMc2Stopped) { Log.d("Engine_Driver", "ESU_MCII: Device button presses whilst stopped ignored"); Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastEsuMc2NoButtonPresses), Toast.LENGTH_SHORT).show(); return; } EsuMc2ButtonAction buttonAction; switch (buttonNo) { case MobileControl2.KEYCODE_TOP_LEFT: buttonAction = EsuMc2ButtonAction.getAction(prefs.getString("prefEsuMc2ButtonTL", getApplicationContext().getResources().getString(R.string.prefEsuMc2ButtonTLDefaultValue))); break; case MobileControl2.KEYCODE_BOTTOM_LEFT: buttonAction = EsuMc2ButtonAction.getAction(prefs.getString("prefEsuMc2ButtonBL", getApplicationContext().getResources().getString(R.string.prefEsuMc2ButtonBLDefaultValue))); break; case MobileControl2.KEYCODE_TOP_RIGHT: buttonAction = EsuMc2ButtonAction.getAction(prefs.getString("prefEsuMc2ButtonTR", getApplicationContext().getResources().getString(R.string.prefEsuMc2ButtonTRDefaultValue))); break; case MobileControl2.KEYCODE_BOTTOM_RIGHT: buttonAction = EsuMc2ButtonAction.getAction(prefs.getString("prefEsuMc2ButtonBR", getApplicationContext().getResources().getString(R.string.prefEsuMc2ButtonBRDefaultValue))); break; default: buttonAction = EsuMc2ButtonAction.NO_ACTION; } if (isActive) { switch (buttonAction) { case NO_ACTION: // Do nothing break; case ALL_STOP: if ((action == ACTION_DOWN) && (repeatCnt == 0)) { speedUpdateAndNotify(0); // update all three throttles } break; case STOP: if ((action == ACTION_DOWN) && (repeatCnt == 0)) { speedUpdateAndNotify(whichThrottle, 0); } break; case DIRECTION_FORWARD: if (!isScreenLocked && (action == ACTION_DOWN) && (repeatCnt == 0)) { changeDirectionIfAllowed(whichThrottle, DIRECTION_FORWARD); } break; case DIRECTION_REVERSE: if (!isScreenLocked && (action == ACTION_DOWN) && (repeatCnt == 0)) { changeDirectionIfAllowed(whichThrottle, DIRECTION_REVERSE); } break; case DIRECTION_TOGGLE: if (!isScreenLocked && (action == ACTION_DOWN) && (repeatCnt == 0)) { if ((getDirection(whichThrottle) == DIRECTION_FORWARD)) { changeDirectionIfAllowed(whichThrottle, DIRECTION_REVERSE); } else { changeDirectionIfAllowed(whichThrottle, DIRECTION_FORWARD); } } break; case SPEED_INCREASE: if (!isScreenLocked && action == ACTION_DOWN) { if (repeatCnt == 0) { esuButtonAutoIncrement = true; esuButtonRepeatUpdateHandler.post(new EsuMc2ButtonRptUpdater(whichThrottle)); } } break; case SPEED_DECREASE: if (!isScreenLocked && action == ACTION_DOWN) { if (repeatCnt == 0) { esuButtonAutoDecrement = true; esuButtonRepeatUpdateHandler.post(new EsuMc2ButtonRptUpdater(whichThrottle)); } } break; case NEXT_THROTTLE: if (!isScreenLocked && (action == ACTION_DOWN) && (repeatCnt == 0)) { setNextActiveThrottle(); } break; case FN00: case FN01: case FN02: case FN03: case FN04: case FN05: case FN06: case FN07: case FN08: case FN09: case FN10: case FN11: case FN12: case FN13: case FN14: case FN15: case FN16: case FN17: case FN18: case FN19: case FN20: case FN21: case FN22: case FN23: case FN24: case FN25: case FN26: case FN27: case FN28: if (!isScreenLocked && repeatCnt == 0) { if (action == ACTION_DOWN) { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle), buttonAction.getFunction(), 1); } else { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle), buttonAction.getFunction(), 0); } } break; default: // Do nothing break; } } } // private void setEnabledEsuMc2ThrottleScreenButtons(char whichThrottle, boolean enabled) { private void setEnabledEsuMc2ThrottleScreenButtons(int whichThrottle, boolean enabled) { // Disables/enables seekbar and speed buttons bLSpds[whichThrottle].setEnabled(enabled); bRSpds[whichThrottle].setEnabled(enabled); sbs[whichThrottle].setEnabled(enabled); } /** * Display the ESU MCII knob action button if configured * * @param menu the menu upon which the action should be shown */ private void displayEsuMc2KnobMenuButton(Menu menu) { MenuItem mi = menu.findItem(R.id.EsuMc2Knob_button); if (mi == null) return; if (prefs.getBoolean("prefEsuMc2KnobButtonDisplay", false)) { mi.setVisible(true); } else { mi.setVisible(false); } setEsuMc2KnobButton(menu); } /** * Set the state of the ESU MCII knob action button/menu entry * * @param menu the menu upon which the action is shown */ public void setEsuMc2KnobButton(Menu menu) { if (menu != null) { if (isEsuMc2KnobEnabled) { menu.findItem(R.id.EsuMc2Knob_button).setIcon(R.drawable.esumc2knob_on); } else { menu.findItem(R.id.EsuMc2Knob_button).setIcon(R.drawable.esumc2knob_off); } } } /** * Toggle the state of the ESU MCII knob * * @param activity the requesting activity * @param menu the menu upon which the entry/button should be updated */ public void toggleEsuMc2Knob(Activity activity, Menu menu) { if (isEsuMc2KnobEnabled) { isEsuMc2KnobEnabled = false; } else { isEsuMc2KnobEnabled = true; } setEsuMc2KnobButton(menu); } // Listeners for the Select Loco buttons protected class select_function_button_touch_listener implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener { int whichThrottle; protected select_function_button_touch_listener(int new_whichThrottle) { whichThrottle = new_whichThrottle; } @Override public void onClick(View v) { if (IS_ESU_MCII && isEsuMc2Stopped) { Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastEsuMc2NoLocoChange), Toast.LENGTH_SHORT).show(); } else if (isSelectLocoAllowed(whichThrottle)) { // don't loco change while moving if the preference is set start_select_loco_activity(whichThrottle); // pass throttle # setActiveThrottle(whichThrottle); // set the throttle the volume keys control depending on the preference } else { Toast.makeText(getApplicationContext(), getApplicationContext().getResources().getString(R.string.toastLocoChangeNotAllowed), Toast.LENGTH_SHORT).show(); } } @Override public boolean onLongClick(View v) { start_consist_lights_edit(whichThrottle); setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference return true; } //TODO: This onTouch may be redundant now that the gesture overlay is working better @Override public boolean onTouch(View v, MotionEvent event) { int pos[] = new int[2]; v.getLocationOnScreen(pos); // if gesture in progress, we may need to skip button processing if (gestureInProgress) { // Log.d("Engine_Driver", "onTouch " + "Gesture- currentY: " + (pos[1]+event.getY()) + " startY: " + gestureStartY); //check to see if we have a substantial vertical movement return Math.abs(pos[1] + event.getY() - gestureStartY) > (threaded_application.min_fling_distance); } return false; } } //listeners for the increase/decrease speed buttons (not the slider) protected class arrow_speed_button_touch_listener implements View.OnClickListener, View.OnLongClickListener, View.OnTouchListener { int whichThrottle; String arrowDirection; protected arrow_speed_button_touch_listener(int new_whichThrottle, String new_arrowDirection) { whichThrottle = new_whichThrottle; arrowDirection = new_arrowDirection; } @Override public boolean onLongClick(View v) { if (arrowDirection.equals("right")) { mAutoIncrement = true; } else { mAutoDecrement = true; } repeatUpdateHandler.post(new RptUpdater(whichThrottle)); setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference return false; } @Override public void onClick(View v) { if (arrowDirection.equals("right")) { mAutoIncrement = false; incrementSpeed(whichThrottle, SPEED_COMMAND_FROM_BUTTONS); } else { mAutoDecrement = false; decrementSpeed(whichThrottle, SPEED_COMMAND_FROM_BUTTONS); } setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference } @Override public boolean onTouch(View v, MotionEvent event) { if ((event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) && mAutoIncrement) { mAutoIncrement = false; } if ((event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) && mAutoDecrement) { mAutoDecrement = false; } setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference return false; } } // Listeners for the direction buttons private class direction_button_touch_listener implements View.OnTouchListener { int function; int whichThrottle; private direction_button_touch_listener(int new_function, int new_whichThrottle) { function = new_function; // store these values for this button whichThrottle = new_whichThrottle; } private void doButtonPress() { switch (this.function) { case direction_button.LEFT: if (!directionButtonsAreCurrentlyReversed(whichThrottle)) { changeDirectionIfAllowed(whichThrottle, 1); } else { changeDirectionIfAllowed(whichThrottle, 0); } break; case direction_button.RIGHT: { if (!directionButtonsAreCurrentlyReversed(whichThrottle)) { changeDirectionIfAllowed(whichThrottle, 0); } else { changeDirectionIfAllowed(whichThrottle, 1); } break; } } setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference } Runnable run = new Runnable() { @Override public void run() { if (isDirectionButtonLongPress) { if (isChangeDirectionAllowed(whichThrottle)) { // only respond to the long click if it is ok to change directions doButtonPress(); //in case the the direction button long clicked is not the current direction change the direction first if (prefSwapForwardReverseButtonsLongPress) { //v.playSoundEffect(SoundEffectConstants.CLICK); currentSwapForwardReverseButtons[whichThrottle] = !currentSwapForwardReverseButtons[whichThrottle]; setDirectionButtonLabels(); Toast.makeText(getApplicationContext(), getApplicationContext().getResources() .getString(R.string.toastDirectionButtonsSwapped), Toast.LENGTH_SHORT).show(); } doButtonPress(); } setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference } } }; @Override public boolean onTouch(final View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { isDirectionButtonLongPress = true; directionButtonLongPressHandler.postDelayed(run, prefDirectionButtonLongPressDelay); // Log.d("Engine_Driver", "onTouch direction " + function + " action " + // event.getAction()); v.playSoundEffect(SoundEffectConstants.CLICK); // make the click sound once doButtonPress(); setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference } else if (event.getAction() == MotionEvent.ACTION_UP) { isDirectionButtonLongPress = false; directionButtonLongPressHandler.removeCallbacks(run); } return true; } } private int isFunctionLeadTrailFollow(int whichThrottle, int fKey, String lab) { //String lab = mainapp.function_labels[whichThrottle].get(fKey).toUpperCase().trim(); boolean lead = false; boolean trail = false; boolean followLeadFunction = false; if (prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) { // boolean selectiveLeadSound = prefs.getBoolean("SelectiveLeadSound", getResources().getBoolean(R.bool.prefSelectiveLeadSoundDefaultValue)); if (!lab.equals("")) { lead = (prefSelectiveLeadSound && (lab.contains(FUNCTION_BUTTON_LOOK_FOR_WHISTLE) || lab.contains(FUNCTION_BUTTON_LOOK_FOR_HORN) || lab.contains(FUNCTION_BUTTON_LOOK_FOR_BELL)) || lab.contains(FUNCTION_BUTTON_LOOK_FOR_HEAD) || (lab.contains(FUNCTION_BUTTON_LOOK_FOR_LIGHT) && !lab.contains(FUNCTION_BUTTON_LOOK_FOR_REAR))); followLeadFunction = (lab.contains(FUNCTION_BUTTON_LOOK_FOR_LIGHT)); trail = lab.contains(FUNCTION_BUTTON_LOOK_FOR_REAR); } // boolean selectiveLeadSoundF1 = prefs.getBoolean("SelectiveLeadSoundF1", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF1DefaultValue)); // boolean selectiveLeadSoundF2 = prefs.getBoolean("SelectiveLeadSoundF2", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF2DefaultValue)); if ((prefSelectiveLeadSound) && (fKey == 1) && (prefSelectiveLeadSoundF1)) { lead = true; } if ((prefSelectiveLeadSound) && (fKey == 2) && (prefSelectiveLeadSoundF2)) { lead = true; } } if ((lead) && (followLeadFunction)) { return FUNCTION_IS_LEAD_AND_FOLLOW; } if ((lead) && (trail)) { return FUNCTION_IS_LEAD_AND_TRAIL; } if (lead) { return FUNCTION_IS_LEAD_ONLY; } if (trail) { return FUNCTION_IS_TRAIL_ONLY; } if (followLeadFunction) { return FUNCTION_IS_FOLLOW; } return 0; } private void sendFunctionToConsistLocos(int whichThrottle, int function, String lab, int buttonPressMessageType, boolean leadOnly, boolean trailOnly, boolean followLeadFunction) { Consist con; con = mainapp.consists[whichThrottle]; if (prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) { String addr = ""; if (leadOnly) addr = con.getLeadAddr(); // ***future else if (trailOnly) // addr = con.getTrailAddr(); //mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, whichThrottle + addr, this.function, 1); if (buttonPressMessageType == BUTTON_PRESS_MESSAGE_TOGGLE) { mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + addr, function); } else { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + addr, function, buttonPressMessageType); } // set_function_request(whichThrottle, function, 1); if (followLeadFunction) { for (Consist.ConLoco l : con.getLocos()) { if (!l.getAddress().equals(con.getLeadAddr())) { // ignore the lead as we have already set it if (l.isLightOn() == LIGHT_FOLLOW) { if (buttonPressMessageType == BUTTON_PRESS_MESSAGE_TOGGLE) { mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + l.getAddress(), function); } else { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + l.getAddress(), function, buttonPressMessageType); } } } } } } else { if (buttonPressMessageType == BUTTON_PRESS_MESSAGE_TOGGLE) { mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + con.getLeadAddr(), function); } else { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + con.getLeadAddr(), function, buttonPressMessageType); } function = getFunctionNumber(con, function, lab); List<Integer> functionList = new ArrayList<>(); for (Consist.ConLoco l : con.getLocos()) { if (!l.getAddress().equals(con.getLeadAddr())) { // ignore the lead as we have already set it functionList = l.getMatchingFunctions(function, lab, l.getAddress().equals(con.getLeadAddr()), l.getAddress().equals(con.getTrailAddr()), prefConsistFollowDefaultAction, prefConsistFollowStrings, prefConsistFollowActions, prefConsistFollowHeadlights); if (functionList.size() > 0) { for (int i = 0; i < functionList.size(); i++) { if (buttonPressMessageType == BUTTON_PRESS_MESSAGE_TOGGLE) { mainapp.toggleFunction(mainapp.throttleIntToString(i) + l.getAddress(), functionList.get(i)); } else { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + l.getAddress(), function, buttonPressMessageType); } } } } } } } private int getFunctionNumber(Consist con, int functionNumber, String lab) { int result = functionNumber; if (functionNumber == -1) { // find the function number for this if we don't already have it for (Consist.ConLoco l : con.getLocos()) { if (l.getAddress().equals(con.getLeadAddr())) { result = l.getFunctionNumberFromLabel(lab); } } } return result; } protected class function_button_touch_listener implements View.OnTouchListener { int function; int whichThrottle; boolean leadOnly; // function only applies to the lead loco boolean trailOnly; // function only applies to the trail loco (future) boolean followLeadFunction; // function only applies to the locos that have been set to follow the function String lab; Integer functionNumber = -1; protected function_button_touch_listener(int new_function, int new_whichThrottle) { this(new_function, new_whichThrottle, ""); } protected function_button_touch_listener(int new_function, int new_whichThrottle, String funcLabel) { function = new_function; // store these values for this button whichThrottle = new_whichThrottle; lab = funcLabel.toUpperCase().trim(); leadOnly = false; trailOnly = false; followLeadFunction = false; int result = isFunctionLeadTrailFollow(whichThrottle, function, lab); if ((result == FUNCTION_IS_LEAD_ONLY) || (result == FUNCTION_IS_LEAD_AND_FOLLOW) || (result == FUNCTION_IS_LEAD_AND_TRAIL)) { leadOnly = true; } if (result == FUNCTION_IS_TRAIL_ONLY) { trailOnly = true; } if ((result == FUNCTION_IS_FOLLOW) || (result == FUNCTION_IS_LEAD_AND_FOLLOW)) { followLeadFunction = true; } // if (prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) { // boolean selectiveLeadSound = prefs.getBoolean("SelectiveLeadSound", getResources().getBoolean(R.bool.prefSelectiveLeadSoundDefaultValue)); // if (!lab.equals("")) { // leadOnly = (selectiveLeadSound && // (lab.contains(FUNCTION_BUTTON_LOOK_FOR_WHISTLE) || lab.contains(FUNCTION_BUTTON_LOOK_FOR_HORN) || lab.contains(FUNCTION_BUTTON_LOOK_FOR_BELL)) // || lab.contains(FUNCTION_BUTTON_LOOK_FOR_HEAD) // || (lab.contains(FUNCTION_BUTTON_LOOK_FOR_LIGHT) && !lab.contains(FUNCTION_BUTTON_LOOK_FOR_REAR))); // followLeadFunction = (lab.contains(FUNCTION_BUTTON_LOOK_FOR_LIGHT)); // trailOnly = lab.contains(FUNCTION_BUTTON_LOOK_FOR_REAR); // } // boolean selectiveLeadSoundF1 = prefs.getBoolean("SelectiveLeadSoundF1", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF1DefaultValue)); // boolean selectiveLeadSoundF2 = prefs.getBoolean("SelectiveLeadSoundF2", getResources().getBoolean(R.bool.prefSelectiveLeadSoundF2DefaultValue)); // if ((selectiveLeadSound) && (new_function == 1) && (selectiveLeadSoundF1)) { // leadOnly = true; // } // if ((selectiveLeadSound) && (new_function == 2) && (selectiveLeadSoundF2)) { // leadOnly = true; // } // } } @Override public boolean onTouch(View v, MotionEvent event) { // Log.d("Engine_Driver", "onTouch func " + function + " action " + // event.getAction()); // make the click sound once if (event.getAction() == MotionEvent.ACTION_DOWN) { v.playSoundEffect(SoundEffectConstants.CLICK); } // if gesture in progress, skip button processing if (gestureInProgress) { return (true); } // if gesture just failed, insert one DOWN event on this control if (gestureFailed) { handleAction(MotionEvent.ACTION_DOWN); gestureFailed = false; // just do this once } handleAction(event.getAction()); return (true); } // private void getFunctionNumber(Consist con) { // if (functionNumber==-1) { // find the function number for this if we don't already have it // for (Consist.ConLoco l : con.getLocos()) { // if (l.getAddress().equals(con.getLeadAddr())) { // functionNumber=l.getFunctionNumberFromLabel(lab); // } // } // } // // } private void handleAction(int action) { switch (action) { case MotionEvent.ACTION_DOWN: { switch (this.function) { case function_button.STOP: set_stop_button(whichThrottle, true); speedUpdateAndNotify(whichThrottle, 0); break; case function_button.SPEED_LABEL: // specify which throttle the volume button controls if (getConsist(whichThrottle).isActive() && !(IS_ESU_MCII && isEsuMc2Stopped)) { // only assign if Active and, if an ESU MCII not in Stop mode whichVolume = whichThrottle; set_labels(); } if (IS_ESU_MCII && isEsuMc2Stopped) { Toast.makeText(getApplicationContext(), getApplicationContext().getResources() .getText(R.string.toastEsuMc2NoThrottleChange), Toast.LENGTH_SHORT).show(); } break; default: { // handle the function buttons sendFunctionToConsistLocos(whichThrottle, function, lab, BUTTON_PRESS_MESSAGE_TOGGLE, leadOnly, trailOnly, followLeadFunction); // Consist con; // con = mainapp.consists[whichThrottle]; // // if (prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) { // // String addr = ""; // if (leadOnly) // addr = con.getLeadAddr(); // // ***future else if (trailOnly) // // addr = con.getTrailAddr(); // // //mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, whichThrottle + addr, this.function, 1); // mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + addr, this.function); // // set_function_request(whichThrottle, function, 1); // // if (followLeadFunction) { // for (Consist.ConLoco l : con.getLocos()) { // if (!l.getAddress().equals(con.getLeadAddr())) { // ignore the lead as we have already set it // if (l.isLightOn() == LIGHT_FOLLOW) { // mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + l.getAddress(), this.function); // } // } // } // } // } else { // mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + con.getLeadAddr(), this.function); // // getFunctionNumber(con); // // List<Integer> functionList = new ArrayList<>(); // for (Consist.ConLoco l : con.getLocos()) { // if (!l.getAddress().equals(con.getLeadAddr())) { // ignore the lead as we have already set it // functionList = l.getMatchingFunctions(functionNumber, lab, l.getAddress().equals(con.getLeadAddr()), l.getAddress().equals(con.getTrailAddr()), prefConsistFollowDefaultAction, prefConsistFollowStrings, prefConsistFollowActions, prefConsistFollowHeadlights); // if (functionList.size()>0) { // for (int i = 0; i < functionList.size(); i++) { // mainapp.toggleFunction(mainapp.throttleIntToString(i) + l.getAddress(), functionList.get(i)); // } // } // } // } // } // break; } } break; } // handle stopping of function on key-up case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (function == function_button.STOP) { set_stop_button(whichThrottle, false); } // only process UP event if this is a "function" button else if (function < direction_button.LEFT) { Consist con = mainapp.consists[whichThrottle]; String addr = ""; if (prefConsistFollowRuleStyle.equals(CONSIST_FUNCTION_RULE_STYLE_ORIGINAL)) { if (leadOnly) addr = con.getLeadAddr(); // ***future else if (trailOnly) // addr = con.getTrailAddr(); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + addr, function, 0); // set_function_request(whichThrottle, function, 0); } else { //mainapp.toggleFunction(mainapp.throttleIntToString(whichThrottle) + con.getLeadAddr(), this.function); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + con.getLeadAddr(), function, 0); functionNumber = getFunctionNumber(con, functionNumber, lab); List<Integer> functionList = new ArrayList<>(); for (Consist.ConLoco l : con.getLocos()) { if (!l.getAddress().equals(con.getLeadAddr())) { // ignore the lead as we have already set it functionList = l.getMatchingFunctions(functionNumber, lab, l.getAddress().equals(con.getLeadAddr()), l.getAddress().equals(con.getTrailAddr()), prefConsistFollowDefaultAction, prefConsistFollowStrings, prefConsistFollowActions, prefConsistFollowHeadlights); if (functionList.size() > 0) { for (int i = 0; i < functionList.size(); i++) { //mainapp.toggleFunction(mainapp.throttleIntToString(i) + l.getAddress(), i); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.FUNCTION, mainapp.throttleIntToString(whichThrottle) + l.getAddress(), functionList.get(i), 0); } } } } } } break; } setActiveThrottle(whichThrottle); // set the throttle the volmue keys control depending on the preference } } //Listeners for the throttle slider protected class throttle_listener implements SeekBar.OnSeekBarChangeListener, View.OnTouchListener { int whichThrottle; int lastSpeed; boolean limitedJump; int jumpSpeed; protected throttle_listener(int new_whichThrottle) { whichThrottle = new_whichThrottle; // store values for this listener lastSpeed = 0; limitedJump = false; } @Override public boolean onTouch(View v, MotionEvent event) { // Log.d("Engine_Driver", "onTouchThrot action " + event.getAction()); // consume event if gesture is in progress, otherwise pass it to the SeekBar onProgressChanged() return (gestureInProgress); } @Override public void onProgressChanged(SeekBar throttle, int speed, boolean fromUser) { // limit speed change if change was initiated by a user slider touch (prevents "bouncing") if (fromUser) { if (!limitedJump) { // touch generates multiple onProgressChanged events, skip processing after first limited jump if ((speed - lastSpeed) > max_throttle_change) { // if jump is too large then limit it // Log.d("Engine_Driver", "onProgressChanged -- throttling change"); mAutoIncrement = true; // advance slowly jumpSpeed = speed; // save ultimate target value limitedJump = true; throttle.setProgress(lastSpeed); repeatUpdateHandler.post(new RptUpdater(whichThrottle)); return; } sendSpeedMsg(whichThrottle, speed); setDisplayedSpeed(whichThrottle, speed); } else { // got a touch while processing limitJump speed = lastSpeed; // so suppress multiple touches throttle.setProgress(lastSpeed); } // Now update ESU MCII Knob position if (IS_ESU_MCII) { setEsuThrottleKnobPosition(whichThrottle, speed); } setActiveThrottle(whichThrottle); // set the throttle the volume keys control depending on the preference } else { if (limitedJump) { if (speed >= jumpSpeed) { // stop when we reach the target mAutoIncrement = false; limitedJump = false; throttle.setProgress(jumpSpeed); } } setDisplayedSpeed(whichThrottle, speed); } if (speed == 0 || lastSpeed == 0) { // check rules when going to/from 0 speed applySpeedRelatedOptions(whichThrottle); } lastSpeed = speed; } @Override public void onStartTrackingTouch(SeekBar sb) { gestureInProgress = false; limitedJump = false; } @Override public void onStopTrackingTouch(SeekBar sb) { mAutoIncrement = false; } } // send a throttle speed message to WiT public void sendSpeedMsg(int whichThrottle, int speed) { // start timer to briefly ignore WiT speed messages - avoids speed "jumping" changeTimers[whichThrottle].changeDelay(); // send speed update to WiT mainapp.sendMsg(mainapp.comm_msg_handler, message_type.VELOCITY, "", whichThrottle, speed); } // implement delay for briefly ignoring WiT speed reports after sending a throttle speed update // this prevents use of speed reports sent by WiT just prior to WiT processing the speed update protected class ChangeDelay { boolean delayInProg; Runnable changeTimer; int whichThrottle; protected ChangeDelay(int wThrot) { delayInProg = false; changeTimer = new ChangeTimer(); whichThrottle = wThrot; } private void changeDelay() { if (mainapp.throttle_msg_handler == null) return; mainapp.throttle_msg_handler.removeCallbacks(changeTimer); //remove any pending requests delayInProg = true; mainapp.throttle_msg_handler.postDelayed(changeTimer, changeDelay); } class ChangeTimer implements Runnable { private ChangeTimer() { delayInProg = false; } @Override public void run() { // change delay is over - clear the delay flag delayInProg = false; } } } // set the title, optionally adding the current time. public void setTitle() { if (mainapp.displayClock) setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle_short) + " " + currentTime); else setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle)); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @SuppressLint({ "Recycle", "SetJavaScriptEnabled", "ClickableViewAccessibility" }) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { try { // restore the requested throttle direction so we can update the // direction indication while we wait for an update from WiT for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottlesCurrentScreen; throttleIndex++) { if (savedInstanceState .getSerializable("dir" + mainapp.throttleIntToString(throttleIndex)) != null) dirs[throttleIndex] = (int) savedInstanceState.getSerializable("dir" + throttleIndex); } } catch (Exception ignored) { // log the error, but otherwise keep going. Log.d("Engine_Driver", "Restore of saved instance state failed " + android.os.Build.VERSION.SDK_INT); } } mainapp = (threaded_application) this.getApplication(); prefs = getSharedPreferences("jmri.enginedriver_preferences", 0); if (mainapp.isForcingFinish()) { // expedite mainapp.appIsFinishing = true; return; } mainapp.applyTheme(this); setTitle(getApplicationContext().getResources().getString(R.string.app_name_throttle)); // needed in case the language was changed from the default // setContentView(R.layout.throttle); setContentView(layoutViewId); getCommonPrefs(true); // get all the common preferences setThottleNumLimits(); getDirectionButtonPrefs(); webViewIsOn = !webViewLocation.equals(WEB_VIEW_LOCATION_NONE); keepWebViewLocation = webViewLocation; isScreenLocked = false; // get the screen brightness on create screenBrightnessOriginal = getScreenBrightness(); screenBrightnessModeOriginal = getScreenBrightnessMode(); // myGesture = new GestureDetector(this); GestureOverlayView ov = findViewById(R.id.throttle_overlay); ov.addOnGestureListener(this); ov.setGestureVisible(false); direction_button_touch_listener dbtl; function_button_touch_listener fbtl; select_function_button_touch_listener sfbt; arrow_speed_button_touch_listener asbl; bSels = new Button[mainapp.maxThrottles]; bRSpds = new Button[mainapp.maxThrottles]; bLSpds = new Button[mainapp.maxThrottles]; tvLeftDirInds = new TextView[mainapp.maxThrottles]; tvRightDirInds = new TextView[mainapp.maxThrottles]; bFwds = new Button[mainapp.maxThrottles]; bStops = new Button[mainapp.maxThrottles]; bRevs = new Button[mainapp.maxThrottles]; sbs = new SeekBar[mainapp.maxThrottles]; tvGamePads = new TextView[mainapp.maxThrottles]; tvSpdLabs = new TextView[mainapp.maxThrottles]; tvSpdVals = new TextView[mainapp.maxThrottles]; tvVols = new TextView[mainapp.maxThrottles]; lls = new LinearLayout[mainapp.maxThrottles]; llSetSpds = new LinearLayout[mainapp.maxThrottles]; llLocoIds = new LinearLayout[mainapp.maxThrottles]; llLocoDirs = new LinearLayout[mainapp.maxThrottles]; fbs = new ViewGroup[mainapp.maxThrottles]; tops = new int[mainapp.maxThrottles]; bottoms = new int[mainapp.maxThrottles]; functionMaps = (LinkedHashMap<Integer, Button>[]) new LinkedHashMap<?, ?>[mainapp.maxThrottles]; displayUnitScales = new double[mainapp.maxThrottles]; changeTimers = new ChangeDelay[mainapp.maxThrottles]; // throttle layouts vThrotScr = findViewById(R.id.throttle_screen); vThrotScrWrap = findViewById(R.id.throttle_screen_wrapper); for (int i = 0; i < mainapp.maxThrottles; i++) { // set listener for select loco buttons Button bSel = findViewById(R.id.button_select_loco_0); TextView tvLeft = findViewById(R.id.loco_left_direction_indicaton_0); TextView tvRight = findViewById(R.id.loco_right_direction_indicaton_0); switch (i) { case 1: bSel = findViewById(R.id.button_select_loco_1); tvLeft = findViewById(R.id.loco_left_direction_indicaton_1); tvRight = findViewById(R.id.loco_right_direction_indicaton_1); break; case 2: bSel = findViewById(R.id.button_select_loco_2); tvLeft = findViewById(R.id.loco_left_direction_indicaton_2); tvRight = findViewById(R.id.loco_right_direction_indicaton_2); break; case 3: bSel = findViewById(R.id.button_select_loco_3); tvLeft = findViewById(R.id.loco_left_direction_indicaton_3); tvRight = findViewById(R.id.loco_right_direction_indicaton_3); break; case 4: bSel = findViewById(R.id.button_select_loco_4); tvLeft = findViewById(R.id.loco_left_direction_indicaton_4); tvRight = findViewById(R.id.loco_right_direction_indicaton_4); break; case 5: bSel = findViewById(R.id.button_select_loco_5); tvLeft = findViewById(R.id.loco_left_direction_indicaton_5); tvRight = findViewById(R.id.loco_right_direction_indicaton_5); break; } bSels[i] = bSel; bSels[i].setClickable(true); sfbt = new select_function_button_touch_listener(i); bSels[i].setOnClickListener(sfbt); bSels[i].setOnTouchListener(sfbt); bSels[i].setOnLongClickListener(sfbt); // Consist Light Edit tvLeftDirInds[i] = tvLeft; tvRightDirInds[i] = tvRight; // Arrow Keys try { Button bRight = findViewById(R.id.right_speed_button_0); Button bLeft = findViewById(R.id.left_speed_button_0); switch (i) { case 1: bRight = findViewById(R.id.right_speed_button_1); bLeft = findViewById(R.id.left_speed_button_1); break; case 2: bRight = findViewById(R.id.right_speed_button_2); bLeft = findViewById(R.id.left_speed_button_2); break; case 3: bRight = findViewById(R.id.right_speed_button_3); bLeft = findViewById(R.id.left_speed_button_3); break; case 4: bRight = findViewById(R.id.right_speed_button_4); bLeft = findViewById(R.id.left_speed_button_4); break; case 5: bRight = findViewById(R.id.right_speed_button_5); bLeft = findViewById(R.id.left_speed_button_5); break; } bRSpds[i] = bRight; bRSpds[i].setClickable(true); asbl = new arrow_speed_button_touch_listener(i, "right"); bRSpds[i].setOnLongClickListener(asbl); bRSpds[i].setOnTouchListener(asbl); bRSpds[i].setOnClickListener(asbl); bLSpds[i] = bLeft; bLSpds[i].setClickable(true); asbl = new arrow_speed_button_touch_listener(i, "left"); bLSpds[i].setOnLongClickListener(asbl); bLSpds[i].setOnTouchListener(asbl); bLSpds[i].setOnClickListener(asbl); } catch (Exception ex) { Log.d("debug", "onCreate: " + ex.getMessage()); } // set listeners for 3 direction buttons for each throttle //---------------------------------------- Button bFwd = findViewById(R.id.button_fwd_0); Button bStop = findViewById(R.id.button_stop_0); Button bRev = findViewById(R.id.button_rev_0); View v = findViewById(R.id.speed_cell_0); switch (i) { case 1: bFwd = findViewById(R.id.button_fwd_1); bStop = findViewById(R.id.button_stop_1); bRev = findViewById(R.id.button_rev_1); v = findViewById(R.id.speed_cell_1); break; case 2: bFwd = findViewById(R.id.button_fwd_2); bStop = findViewById(R.id.button_stop_2); bRev = findViewById(R.id.button_rev_2); v = findViewById(R.id.speed_cell_2); break; case 3: bFwd = findViewById(R.id.button_fwd_3); bStop = findViewById(R.id.button_stop_3); bRev = findViewById(R.id.button_rev_3); v = findViewById(R.id.speed_cell_3); break; case 4: bFwd = findViewById(R.id.button_fwd_4); bStop = findViewById(R.id.button_stop_4); bRev = findViewById(R.id.button_rev_4); v = findViewById(R.id.speed_cell_4); break; case 5: bFwd = findViewById(R.id.button_fwd_5); bStop = findViewById(R.id.button_stop_5); bRev = findViewById(R.id.button_rev_5); v = findViewById(R.id.speed_cell_5); break; } bFwds[i] = bFwd; dbtl = new direction_button_touch_listener(direction_button.LEFT, i); bFwds[i].setOnTouchListener(dbtl); bStops[i] = bStop; fbtl = new function_button_touch_listener(function_button.STOP, i); bStops[i].setOnTouchListener(fbtl); bRevs[i] = bRev; dbtl = new direction_button_touch_listener(direction_button.RIGHT, i); bRevs[i].setOnTouchListener(dbtl); fbtl = new function_button_touch_listener(function_button.SPEED_LABEL, i); v.setOnTouchListener(fbtl); // set up listeners for all throttles SeekBar s = findViewById(R.id.speed_0); switch (i) { case 1: s = findViewById(R.id.speed_1); break; case 2: s = findViewById(R.id.speed_2); break; case 3: s = findViewById(R.id.speed_3); break; case 4: s = findViewById(R.id.speed_4); break; case 5: s = findViewById(R.id.speed_5); break; } throttle_listener thl; sbs[i] = s; thl = new throttle_listener(i); sbs[i].setOnSeekBarChangeListener(thl); sbs[i].setOnTouchListener(thl); max_throttle_change = 1; // displaySpeedSteps = false; switch (i) { case 0: lls[i] = findViewById(R.id.throttle_0); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_0_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_0); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_0); tvVols[i] = findViewById(R.id.volume_indicator_0); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_0); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_0); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_0); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_0); break; case 1: lls[i] = findViewById(R.id.throttle_1); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_1_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_1); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_1); tvVols[i] = findViewById(R.id.volume_indicator_1); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_1); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_1); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_1); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_1); break; case 2: lls[i] = findViewById(R.id.throttle_2); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_2_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_2); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_2); tvVols[i] = findViewById(R.id.volume_indicator_2); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_2); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_2); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_2); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_2); break; case 3: lls[i] = findViewById(R.id.throttle_3); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_3_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_3); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_3); tvVols[i] = findViewById(R.id.volume_indicator_3); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_3); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_3); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_3); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_3); break; case 4: lls[i] = findViewById(R.id.throttle_4); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_4_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_4); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_4); tvVols[i] = findViewById(R.id.volume_indicator_4); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_4); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_4); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_4); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_4); break; case 5: lls[i] = findViewById(R.id.throttle_5); // llSetSpds[i] = (LinearLayout) findViewById(R.id.throttle_5_setspeed); llLocoIds[i] = findViewById(R.id.loco_buttons_group_5); llLocoDirs[i] = findViewById(R.id.dir_buttons_table_5); tvVols[i] = findViewById(R.id.volume_indicator_5); // volume indicators tvGamePads[i] = findViewById(R.id.gamepad_indicator_5); // gamepad indicators tvSpdLabs[i] = findViewById(R.id.speed_label_5); // set_default_function_labels(); tvSpdVals[i] = findViewById(R.id.speed_value_label_5); //**// fbs[i] = (ViewGroup) findViewById(R.id.function_buttons_table_5); break; } // set throttle change delay timers changeTimers[i] = new ChangeDelay(i); } clearVolumeAndGamepadAdditionalIndicators(); setDirectionButtonLabels(); // set all the direction button labels // set label and dcc functions (based on settings) or hide if no label setAllFunctionLabelsAndListeners(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } setActiveThrottle(0); // set the throttle the volume keys control depending on the preference to the default 0 webView = findViewById(R.id.throttle_webview); String databasePath = webView.getContext().getDir("databases", Context.MODE_PRIVATE).getPath(); webView.getSettings().setDatabasePath(databasePath); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setBuiltInZoomControls(true); // Enable Multitouch // if supported webView.getSettings().setUseWideViewPort(true); // Enable greater // zoom-out webView.getSettings().setDefaultZoom(WebSettings.ZoomDensity.FAR); webView.setInitialScale((int) (100 * scale)); // webView.getSettings().setLoadWithOverviewMode(true); // size image to // fill width // open all links inside the current view (don't start external web // browser) noUrl = getApplicationContext().getResources().getString(R.string.blank_page_url); WebViewClient EDWebClient = new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return false; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); if (!noUrl.equals(url)) { // if url is legit currentUrl = url; if (firstUrl == null) { // if this is the first legit url firstUrl = url; clearHistory = true; } if (clearHistory) { // keep clearing history until off this page if (url.equals(firstUrl)) { // (works around Android bug) webView.clearHistory(); } else { clearHistory = false; } } } } }; webView.setWebViewClient(EDWebClient); if (currentUrl == null || savedInstanceState == null || webView.restoreState(savedInstanceState) == null) { load_webview(); // reload if no saved state or no page had loaded when state was saved } // put pointer to this activity's handler in main app's shared variable mainapp.throttle_msg_handler = new throttle_handler(); // set throttle change delay timers for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { changeTimers[throttleIndex] = new ChangeDelay(throttleIndex); } // tone generator for feedback sounds try { tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, preferences.getIntPrefValue(prefs, "prefGamePadFeedbackVolume", getApplicationContext() .getResources().getString(R.string.prefGamePadFeedbackVolumeDefaultValue))); } catch (RuntimeException e) { Log.e("Engine_Driver", "new ToneGenerator failed. Runtime Exception, OS " + android.os.Build.VERSION.SDK_INT + " Message: " + e); } // set GamePad Support setGamepadKeys(); // initialise ESU MCII if (IS_ESU_MCII) { Log.d("Engine_Driver", "ESU_MCII: Initialise fragments..."); int zeroTrim = preferences.getIntPrefValue(prefs, "prefEsuMc2ZeroTrim", getApplicationContext().getResources().getString(R.string.prefEsuMc2ZeroTrimDefaultValue)); esuThrottleFragment = ThrottleFragment.newInstance(zeroTrim); esuThrottleFragment.setOnThrottleListener(esuOnThrottleListener); esuStopButtonFragment = StopButtonFragment.newInstance(); esuStopButtonFragment.setOnStopButtonListener(esuOnStopButtonListener); Log.d("Engine_Driver", "ESU_MCII: ...fragments initialised"); getSupportFragmentManager().beginTransaction().add(esuThrottleFragment, "mc2:throttle") .add(esuStopButtonFragment, "mc2:stopKey").commit(); esuMc2Led.setState(EsuMc2Led.RED, EsuMc2LedState.OFF, true); esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.STEADY_FLASH, true); // Now apply knob zero trim updateEsuMc2ZeroTrim(); Log.d("Engine_Driver", "ESU_MCII: Initialisation complete"); } setupSensor(); // setup the support for shake actions. setupTts(); if (prefs.getBoolean("prefImportServerAuto", getApplicationContext().getResources().getBoolean(R.bool.prefImportServerAutoDefaultValue))) { autoImportFromURL(); } } // end of onCreate() @Override public void onResume() { super.onResume(); mainapp.removeNotification(); if (mainapp.isForcingFinish()) { // expedite mainapp.appIsFinishing = true; this.finish(); overridePendingTransition(0, 0); return; } if (!mainapp.setActivityOrientation(this)) // set screen orientation based on prefs { Intent in = new Intent().setClass(this, web_activity.class); // if autoWeb and landscape, switch to Web activity in.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); return; } navigatingAway = false; currentTime = ""; mainapp.sendMsg(mainapp.comm_msg_handler, message_type.CURRENT_TIME); // request time update // format the screen area for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { enable_disable_buttons(throttleIndex); } gestureFailed = false; gestureInProgress = false; getCommonPrefs(false); getDirectionButtonPrefs(); setThottleNumLimits(); clearVolumeAndGamepadAdditionalIndicators(); getDirectionButtonPrefs(); setDirectionButtonLabels(); // set all the direction button labels setGamepadKeys(); applySpeedRelatedOptions(); // update all throttles set_labels(); // handle labels and update view noUrl = getApplicationContext().getResources().getString(R.string.about_page_url); if (webView != null) { if (!callHiddenWebViewOnResume()) { webView.resumeTimers(); } if (noUrl.equals(webView.getUrl()) && webView.canGoBack()) { //unload static url loaded by onPause webView.goBack(); } } if (mainapp.EStopActivated) { speedUpdateAndNotify(0); // update all three throttles applySpeedRelatedOptions(); // update all three throttles mainapp.EStopActivated = false; } if (IS_ESU_MCII && isEsuMc2Stopped) { if (isEsuMc2AllStopped) { // disable buttons for all throttles for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { setEnabledEsuMc2ThrottleScreenButtons(throttleIndex, false); } } else { // disable buttons for current throttle setEnabledEsuMc2ThrottleScreenButtons(whichVolume, false); } } // update the direction indicators showDirectionIndications(); if (TMenu != null) { for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { switch (throttleIndex) { case 0: TMenu.findItem(R.id.EditConsist0_menu).setVisible(mainapp.consists[0].isMulti()); TMenu.findItem(R.id.EditLightsConsist0_menu).setVisible(mainapp.consists[0].isMulti()); break; case 1: TMenu.findItem(R.id.EditLightsConsist1_menu).setVisible(mainapp.consists[1].isMulti()); TMenu.findItem(R.id.EditConsist1_menu).setVisible(mainapp.consists[1].isMulti()); break; case 2: TMenu.findItem(R.id.EditLightsConsist2_menu).setVisible(mainapp.consists[2].isMulti()); TMenu.findItem(R.id.EditConsist2_menu).setVisible(mainapp.consists[2].isMulti()); break; case 3: TMenu.findItem(R.id.EditLightsConsist3_menu).setVisible(mainapp.consists[3].isMulti()); TMenu.findItem(R.id.EditConsist3_menu).setVisible(mainapp.consists[3].isMulti()); break; case 4: TMenu.findItem(R.id.EditLightsConsist4_menu).setVisible(mainapp.consists[4].isMulti()); TMenu.findItem(R.id.EditConsist4_menu).setVisible(mainapp.consists[4].isMulti()); break; case 5: TMenu.findItem(R.id.EditLightsConsist5_menu).setVisible(mainapp.consists[5].isMulti()); TMenu.findItem(R.id.EditConsist5_menu).setVisible(mainapp.consists[5].isMulti()); break; } } } CookieSyncManager.getInstance().startSync(); if (!prefAccelerometerShake.equals(ACCELERATOROMETER_SHAKE_NONE)) { if (!accelerometerCurrent) { // perference has only just been changed to turn it on setupSensor(); } else { sensorManager.registerListener(shakeDetector, accelerometer, SensorManager.SENSOR_DELAY_UI); } } if (((prefKidsTime > 0) && (kidsTimerRunning != KIDS_TIMER_RUNNNING))) { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.KIDS_TIMER_ENABLE, "", 0, 0); } else { if (kidsTimerRunning == KIDS_TIMER_ENDED) { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.KIDS_TIMER_END, "", 0, 0); } if (prefKidsTimer.equals(PREF_KIDS_TIMER_NONE)) { kidsTimerActions(KIDS_TIMER_DISABLED, 0); } } setupTts(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); webView.saveState(outState); // save history (on rotation) if at least one page has loaded // save the requested throttle direction so we can update the // direction indication immediately in OnCreate following a rotate for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { outState.putSerializable("dir" + mainapp.throttleIntToChar(throttleIndex), dirs[throttleIndex]); } } @Override public void onPause() { super.onPause(); if (webView != null) { if (!callHiddenWebViewOnPause()) { webView.pauseTimers(); } String url = webView.getUrl(); if (url != null && !noUrl.equals(url)) { // if any url has been loaded webView.loadUrl(noUrl); // load a static url to stop any javascript } } CookieSyncManager.getInstance().stopSync(); if (!this.isFinishing() && !navigatingAway) { // only invoke setContentIntentNotification when going into background mainapp.addNotification(this.getIntent()); } if ((isScreenLocked) || (screenDimmed)) { isScreenLocked = false; screenDimmed = false; setScreenBrightness(screenBrightnessOriginal); setScreenBrightnessMode(screenBrightnessModeOriginal); } if (accelerometerCurrent) { sensorManager.unregisterListener(shakeDetector); } } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { setImmersiveModeOn(webView); set_labels(); // need to redraw button Press states since ActionBar and Notification access clears them } } /** * Called when the activity is finished. */ @SuppressWarnings("deprecation") @Override public void onDestroy() { Log.d("Engine_Driver", "throttle.onDestroy()"); if (webView != null) { scale = webView.getScale(); // save current scale for next onCreate } repeatUpdateHandler = null; volumeKeysRepeatUpdateHandler = null; gamepadRepeatUpdateHandler = null; if (IS_ESU_MCII) { esuMc2Led.setState(EsuMc2Led.RED, EsuMc2LedState.OFF); esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.OFF); } if (tg != null) { tg.release(); } mainapp.throttle_msg_handler = null; super.onDestroy(); } private boolean callHiddenWebViewOnPause() { try { Method method = WebView.class.getMethod("onPause"); method.invoke(webView); } catch (Exception e) { return false; } return true; } private boolean callHiddenWebViewOnResume() { try { Method method = WebView.class.getMethod("onResume"); method.invoke(webView); } catch (Exception e) { return false; } return true; } // load the url private void load_webview() { String url = currentUrl; if (webViewLocation.equals(WEB_VIEW_LOCATION_NONE)) { // if not displaying webview webViewIsOn = false; url = noUrl; // load static url to stop javascript currentUrl = null; firstUrl = null; } else if (url == null) // else if initializing { webViewIsOn = true; url = mainapp.createUrl(prefs.getString("InitialThrotWebPage", getApplicationContext().getResources() .getString(R.string.prefInitialThrotWebPageDefaultValue))); if (url == null) { //if port is invalid url = noUrl; } if (firstUrl == null) { scale = initialScale; webView.setInitialScale((int) (100 * scale)); } firstUrl = null; } webView.loadUrl(url); } void setAllFunctionLabelsAndListeners() { for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { set_function_labels_and_listeners_for_view(throttleIndex); } } // helper function to set up function buttons for each throttle // loop through all function buttons and // set label and dcc functions (based on settings) or hide if no label void set_function_labels_and_listeners_for_view(int whichThrottle) { // Log.d("Engine_Driver","starting set_function_labels_and_listeners_for_view"); // // implemented in derived class, but called from this class if (fbs != null) { // if it is null it probably because the Throttle Screen Type does not have Functions Buttons if (fbs[0] != null) { ViewGroup tv; // group ViewGroup r; // row function_button_touch_listener fbtl; Button b; // button int k = 0; // button count LinkedHashMap<Integer, String> function_labels_temp; LinkedHashMap<Integer, Button> functionButtonMap = new LinkedHashMap<>(); tv = fbs[whichThrottle]; // note: we make a copy of function_labels_x because TA might change it // while we are using it (causing issues during button update below) function_labels_temp = mainapp.function_labels_default; if (!prefAlwaysUseDefaultFunctionLabels) { //avoid npe if (mainapp.function_labels != null && mainapp.function_labels[whichThrottle] != null && mainapp.function_labels[whichThrottle].size() > 0) { function_labels_temp = new LinkedHashMap<>(mainapp.function_labels[whichThrottle]); } else { if (mainapp.consists != null) { //avoid npe maybe if (mainapp.consists[whichThrottle] != null && !mainapp.consists[whichThrottle].isLeadFromRoster()) { function_labels_temp = mainapp.function_labels_default; } else { function_labels_temp = mainapp.function_labels_default_for_roster; } } } } // put values in array for indexing in next step // to do this ArrayList<Integer> aList = new ArrayList<>(function_labels_temp.keySet()); if (tv != null) { for (int i = 0; i < tv.getChildCount(); i++) { r = (ViewGroup) tv.getChildAt(i); for (int j = 0; j < r.getChildCount(); j++) { b = (Button) r.getChildAt(j); if (k < function_labels_temp.size()) { Integer func = aList.get(k); functionButtonMap.put(func, b); // save function to button // mapping String bt = function_labels_temp.get(func); fbtl = new function_button_touch_listener(func, whichThrottle, bt); b.setOnTouchListener(fbtl); // if ((mainapp.getCurrentTheme().equals(THEME_DEFAULT))) { // bt = bt + " "; // pad with spaces, and limit to 7 characters // b.setText(bt.substring(0, 7)); // } else { bt = bt + " "; // pad with spaces, and limit to 20 characters b.setText(bt.trim()); // } b.setVisibility(View.VISIBLE); b.setEnabled(false); // start out with everything disabled } else { b.setVisibility(View.GONE); } k++; } } } // update the function-to-button map for the current throttle functionMaps[whichThrottle] = functionButtonMap; } } } // helper function to get a numbered function button from its throttle and function number Button getFunctionButton(char whichThrottle, int func) { Button b; // button LinkedHashMap<Integer, Button> functionButtonMap; functionButtonMap = functionMaps[whichThrottle]; b = functionButtonMap.get(func); return b; } // lookup and set values of various informational text labels and size the // screen elements @SuppressWarnings("deprecation") protected void set_labels() { // Log.d("Engine_Driver","starting set_labels"); if (mainapp.appIsFinishing) { return; } // avoid NPE by not letting this run too early (reported to Play Store) if (tvVols[0] == null) return; // hide or display volume control indicator based on variable setVolumeIndicator(); setGamepadIndicator(); // set up max speeds for throttles int maxThrottle = preferences.getIntPrefValue(prefs, "maximum_throttle_preference", getApplicationContext().getResources().getString(R.string.prefMaximumThrottleDefaultValue)); maxThrottle = (int) Math.round(MAX_SPEED_VAL_WIT * (maxThrottle * .01)); // convert from percent for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { sbs[throttleIndex].setMax(maxThrottle); } // set max allowed change for throttles from prefs int maxChange = preferences.getIntPrefValue(prefs, "maximum_throttle_change_preference", getApplicationContext().getResources().getString(R.string.prefMaximumThrottleChangeDefaultValue)); max_throttle_change = (int) Math.round(maxThrottle * (maxChange * .01)); for (int throttleIndex = 0; throttleIndex < mainapp.maxThrottles; throttleIndex++) { sbs[throttleIndex].setMax(maxThrottle); if (mainapp.consists[throttleIndex].isEmpty()) { maxSpeedSteps[throttleIndex] = 100; } //get speed steps from prefs speedStepPref = preferences.getIntPrefValue(prefs, "DisplaySpeedUnits", getApplicationContext().getResources().getString(R.string.prefDisplaySpeedUnitsDefaultValue)); setDisplayUnitScale(throttleIndex); setDisplayedSpeed(throttleIndex, sbs[throttleIndex].getProgress()); // update numeric speeds since units might have changed } //adjust several items in the menu if (TMenu != null) { mainapp.displayEStop(TMenu); mainapp.setPowerStateButton(TMenu); mainapp.displayPowerStateMenuButton(TMenu); mainapp.setPowerMenuOption(TMenu); mainapp.setWebMenuOption(TMenu); mainapp.setRoutesMenuOption(TMenu); mainapp.setTurnoutsMenuOption(TMenu); mainapp.setGamepadTestMenuOption(TMenu, gamepadCount); mainapp.setKidsMenuOptions(TMenu, prefKidsTimer.equals(PREF_KIDS_TIMER_NONE), gamepadCount); mainapp.setFlashlightButton(TMenu); mainapp.displayFlashlightMenuButton(TMenu); displayEsuMc2KnobMenuButton(TMenu); } vThrotScrWrap.invalidate(); // Log.d("Engine_Driver","ending set_labels"); } @SuppressWarnings("deprecation") @Override public boolean onKeyUp(int key, KeyEvent event) { // int repeatCnt = event.getRepeatCount(); // int action = event.getAction(); // Handle pressing of the back button if (key == KeyEvent.KEYCODE_BACK) { return true; // stop processing this key } if ((key == KEYCODE_VOLUME_UP) || (key == KEYCODE_VOLUME_DOWN)) { mVolumeKeysAutoIncrement = false; mVolumeKeysAutoDecrement = false; return (true); // stop processing this key } return (super.onKeyUp(key, event)); // continue with normal key // processing } @SuppressWarnings("deprecation") @Override public boolean onKeyDown(int key, KeyEvent event) { int repeatCnt = event.getRepeatCount(); // int action = event.getAction(); // Handle pressing of the back button if (key == KEYCODE_BACK) { if (webView.canGoBack() && !clearHistory) { scale = webView.getScale(); // save scale webView.goBack(); webView.setInitialScale((int) (100 * scale)); // restore scale } else if (webView != null) { setImmersiveModeOn(webView); } mainapp.checkExit(this); return (true); // stop processing this key } else if ((key == KEYCODE_VOLUME_UP) || (key == KEYCODE_VOLUME_DOWN)) { // use volume to change speed for specified loco if (!prefDisableVolumeKeys) { // ignore the volume keys if the preference its set for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (throttleIndex == whichVolume && mainapp.consists[throttleIndex].isActive()) { if (key == KEYCODE_VOLUME_UP) { if (repeatCnt == 0) { mVolumeKeysAutoIncrement = true; volumeKeysRepeatUpdateHandler.post(new volumeKeysRptUpdater(throttleIndex)); } } else { if (repeatCnt == 0) { mVolumeKeysAutoDecrement = true; volumeKeysRepeatUpdateHandler.post(new volumeKeysRptUpdater(throttleIndex)); } } } } } return (true); // stop processing this key } return (super.onKeyDown(key, event)); // continue with normal key // processing } private void disconnect() { //loop thru all throttles and send release to server for any that are active for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if (getConsist(throttleIndex).isActive()) { release_loco(throttleIndex); } } webView.stopLoading(); mainapp.appIsFinishing = true; this.finish(); // end this activity overridePendingTransition(0, 0); } // request release of specified throttle void release_loco(int whichThrottle) { mainapp.consists[whichThrottle].release(); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.RELEASE, "", whichThrottle); // pass T, S or G in message } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.throttle_menu, menu); TMenu = menu; mainapp.displayFlashlightMenuButton(menu); mainapp.setFlashlightButton(menu); if (IS_ESU_MCII) { displayEsuMc2KnobMenuButton(menu); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (webView != null) { setImmersiveModeOn(webView); } // Handle all of the possible menu actions. Intent in; switch (item.getItemId()) { case R.id.turnouts_mnu: in = new Intent().setClass(this, turnouts.class); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.push_right_in, R.anim.push_right_out); break; case R.id.routes_mnu: in = new Intent().setClass(this, routes.class); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.push_left_in, R.anim.push_left_out); break; case R.id.web_mnu: in = new Intent().setClass(this, web_activity.class); in.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); navigatingAway = true; mainapp.webMenuSelected = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.exit_mnu: navigatingAway = true; mainapp.checkExit(this); break; case R.id.power_control_mnu: in = new Intent().setClass(this, power_control.class); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.preferences_mnu: in = new Intent().setClass(this, preferences.class); navigatingAway = true; startActivityForResult(in, ACTIVITY_PREFS); // reinitialize function buttons and labels on return connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.settings_mnu: in = new Intent().setClass(this, function_settings.class); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.about_mnu: in = new Intent().setClass(this, about_page.class); navigatingAway = true; startActivity(in); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.logviewer_menu: Intent logviewer = new Intent().setClass(this, LogViewerActivity.class); navigatingAway = true; startActivity(logviewer); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EmerStop: mainapp.sendEStopMsg(); speedUpdate(0); // update all three throttles applySpeedRelatedOptions(); // update all three throttles if (IS_ESU_MCII) { Log.d("Engine_Driver", "ESU_MCII: Move knob request for EStop"); setEsuThrottleKnobPosition(whichVolume, 0); } break; case R.id.power_layout_button: if (!mainapp.isPowerControlAllowed()) { AlertDialog.Builder b = new AlertDialog.Builder(this); b.setIcon(android.R.drawable.ic_dialog_alert); b.setTitle(getApplicationContext().getResources().getString(R.string.powerWillNotWorkTitle)); b.setMessage(getApplicationContext().getResources().getString(R.string.powerWillNotWork)); b.setCancelable(true); b.setNegativeButton("OK", null); AlertDialog alert = b.create(); alert.show(); mainapp.displayPowerStateMenuButton(TMenu); } else { mainapp.powerStateMenuButton(); } break; case R.id.EditConsist0_menu: Intent consistEdit = new Intent().setClass(this, ConsistEdit.class); consistEdit.putExtra("whichThrottle", '0'); navigatingAway = true; startActivityForResult(consistEdit, ACTIVITY_CONSIST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EditConsist1_menu: Intent consistEdit2 = new Intent().setClass(this, ConsistEdit.class); consistEdit2.putExtra("whichThrottle", '1'); navigatingAway = true; startActivityForResult(consistEdit2, ACTIVITY_CONSIST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EditConsist2_menu: Intent consistEdit3 = new Intent().setClass(this, ConsistEdit.class); consistEdit3.putExtra("whichThrottle", '2'); navigatingAway = true; startActivityForResult(consistEdit3, ACTIVITY_CONSIST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EditLightsConsist0_menu: Intent consistLightsEdit = new Intent().setClass(this, ConsistLightsEdit.class); consistLightsEdit.putExtra("whichThrottle", '0'); navigatingAway = true; startActivityForResult(consistLightsEdit, ACTIVITY_CONSIST_LIGHTS); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EditLightsConsist1_menu: Intent consistLightsEdit2 = new Intent().setClass(this, ConsistLightsEdit.class); consistLightsEdit2.putExtra("whichThrottle", '1'); navigatingAway = true; startActivityForResult(consistLightsEdit2, ACTIVITY_CONSIST_LIGHTS); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.EditLightsConsist2_menu: Intent consistLightsEdit3 = new Intent().setClass(this, ConsistLightsEdit.class); consistLightsEdit3.putExtra("whichThrottle", '2'); navigatingAway = true; startActivityForResult(consistLightsEdit3, ACTIVITY_CONSIST_LIGHTS); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.gamepad_test_mnu1: in = new Intent().setClass(this, gamepad_test.class); in.putExtra("whichGamepadNo", "0"); navigatingAway = true; startActivityForResult(in, ACTIVITY_GAMEPAD_TEST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.gamepad_test_mnu2: in = new Intent().setClass(this, gamepad_test.class); in.putExtra("whichGamepadNo", "1"); navigatingAway = true; startActivityForResult(in, ACTIVITY_GAMEPAD_TEST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.gamepad_test_mnu3: in = new Intent().setClass(this, gamepad_test.class); in.putExtra("whichGamepadNo", "2"); navigatingAway = true; startActivityForResult(in, ACTIVITY_GAMEPAD_TEST); connection_activity.overridePendingTransition(this, R.anim.fade_in, R.anim.fade_out); break; case R.id.timer_mnu: showTimerPasswordDialog(); break; case R.id.flashlight_button: mainapp.toggleFlashlight(this, TMenu); break; case R.id.EsuMc2Knob_button: toggleEsuMc2Knob(this, TMenu); break; } return super.onOptionsItemSelected(item); } // handle return from menu items @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case ACTIVITY_SELECT_LOCO: if (resultCode == select_loco.RESULT_LOCO_EDIT) ActivityConsistUpdate(resultCode, data.getExtras()); if ((getConsist(whichVolume) != null) && (!getConsist(whichVolume).isActive())) { setNextActiveThrottle(); // if consist on Volume throttle was released, move to next throttle } else { if (IS_ESU_MCII) { esuMc2Led.setState(EsuMc2Led.GREEN, EsuMc2LedState.STEADY_FLASH, true); } setActiveThrottle(whichVolume); } break; case ACTIVITY_CONSIST: // edit loco or edit consist if (resultCode == ConsistEdit.RESULT_CON_EDIT) ActivityConsistUpdate(resultCode, data.getExtras()); break; case ACTIVITY_CONSIST_LIGHTS: // edit consist lights break; // nothing to do case ACTIVITY_PREFS: { // edit prefs if (resultCode == preferences.RESULT_GAMEPAD) { // gamepad pref changed // update tone generator volume if (tg != null) { tg.release(); try { tg = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, preferences.getIntPrefValue(prefs, "prefGamePadFeedbackVolume", getApplicationContext().getResources() .getString(R.string.prefGamePadFeedbackVolumeDefaultValue))); } catch (RuntimeException e) { Log.e("Engine_Driver", "new ToneGenerator failed. Runtime Exception, OS " + android.os.Build.VERSION.SDK_INT + " Message: " + e); } } // update GamePad Support setGamepadKeys(); } if (resultCode == preferences.RESULT_ESUMCII) { // ESU MCII pref change // update zero trim values updateEsuMc2ZeroTrim(); } // in case the preference has changed but the current screen does not support the number selected. setThottleNumLimits(); getKidsTimerPrefs(); if (prefKidsTimer.equals(PREF_KIDS_TIMER_NONE)) { kidsTimerActions(KIDS_TIMER_DISABLED, 0); } break; } case ACTIVITY_GAMEPAD_TEST: { if (data != null) { String whichGamepadNo = data.getExtras().getString("whichGamepadNo"); if (whichGamepadNo != null) { int result = Integer.valueOf(whichGamepadNo.substring(1, 2)); int gamepadNo = Integer.valueOf(whichGamepadNo.substring(0, 1)); switch (result) { case GAMEPAD_TEST_PASS: gamePadDeviceIdsTested[gamepadNo] = GAMEPAD_GOOD; speakWords(TTS_MSG_GAMEPAD_GAMEPAD_TEST_COMPLETE, ' '); break; case GAMEPAD_TEST_SKIPPED: gamePadDeviceIdsTested[gamepadNo] = GAMEPAD_GOOD; speakWords(TTS_MSG_GAMEPAD_GAMEPAD_TEST_SKIPPED, ' '); break; case GAMEPAD_TEST_FAIL: gamePadDeviceIdsTested[gamepadNo] = GAMEPAD_BAD; speakWords(TTS_MSG_GAMEPAD_GAMEPAD_TEST_FAIL, ' '); break; case GAMEPAD_TEST_RESET: gamepadCount = 0; for (int i = 0; i < gamePadDeviceIds.length; i++) { gamePadDeviceIds[i] = 0; gamePadDeviceIdsTested[i] = 0; } for (int i = 0; i < 3; i++) { gamePadIds[i] = 0; gamePadThrottleAssignment[0] = -1; } mainapp.setGamepadTestMenuOption(TMenu, gamepadCount); setGamepadIndicator(); speakWords(TTS_MSG_GAMEPAD_GAMEPAD_TEST_RESET, ' '); break; default: Log.e("Engine_Driver", "OnActivityResult(ACTIVITY_GAMEPAD_TEST) invalid result!"); } } } else { Log.e("Engine_Driver", "OnActivityResult(ACTIVITY_GAMEPAD_TEST) called with null data!"); } break; } } // loop through all function buttons and // set label and dcc functions (based on settings) or hide if no label setAllFunctionLabelsAndListeners(); set_labels(); } private void ActivityConsistUpdate(int resultCode, Bundle extras) { if (extras != null) { int whichThrottle = mainapp.throttleCharToInt(extras.getChar("whichThrottle")); int dir; int speed; dir = dirs[whichThrottle]; speed = (sbs[whichThrottle] == null ? 0 : sbs[whichThrottle].getProgress()); setEngineDirection(whichThrottle, dir, false); // update direction for each loco in consist sendSpeedMsg(whichThrottle, speed); // ensure all trailing units have the same speed as the lead engine } } // touch events outside the GestureOverlayView get caught here @Override public boolean onTouchEvent(MotionEvent event) { // Log.d("Engine_Driver", "onTouch Title action " + event.getAction()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: gestureStart(event); break; case MotionEvent.ACTION_UP: gestureEnd(event); break; case MotionEvent.ACTION_MOVE: gestureMove(event); break; case MotionEvent.ACTION_CANCEL: gestureCancel(event); } return true; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { // if screen is locked if (isScreenLocked) { // check if we have a swipe up if (ev.getAction() == ACTION_DOWN) { gestureStart(ev); } if (ev.getAction() == ACTION_UP) { gestureEnd(ev); } // otherwise ignore the event return true; } // not locked ... proceed with normal processing return super.dispatchTouchEvent(ev); } @Override public void onGesture(GestureOverlayView arg0, MotionEvent event) { gestureMove(event); } @Override public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) { gestureCancel(event); } // determine if the action was long enough to be a swipe @Override public void onGestureEnded(GestureOverlayView overlay, MotionEvent event) { gestureEnd(event); } @Override public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) { gestureStart(event); } private void gestureStart(MotionEvent event) { gestureStartX = event.getX(); gestureStartY = event.getY(); // Log.d("Engine_Driver", "gestureStart y=" + gestureStartY); // check if the sliders are already hidden by preference if (!prefs.getBoolean("hide_slider_preference", false)) { // if gesture is attempting to start over an enabled slider, ignore it and return immediately. for (int throttleIndex = 0; throttleIndex < mainapp.numThrottles; throttleIndex++) { if ((sbs[throttleIndex].isEnabled() && gestureStartY >= tops[throttleIndex] && gestureStartY <= bottoms[throttleIndex])) { // Log.d("Engine_Driver","exiting gestureStart"); return; } } } gestureInProgress = true; gestureFailed = false; gestureLastCheckTime = event.getEventTime(); mVelocityTracker.clear(); // start the gesture timeout timer if (mainapp.throttle_msg_handler != null) mainapp.throttle_msg_handler.postDelayed(gestureStopped, gestureCheckRate); } public void gestureMove(MotionEvent event) { // Log.d("Engine_Driver", "gestureMove action " + event.getAction()); if (gestureInProgress) { // stop the gesture timeout timer mainapp.throttle_msg_handler.removeCallbacks(gestureStopped); mVelocityTracker.addMovement(event); if ((event.getEventTime() - gestureLastCheckTime) > gestureCheckRate) { // monitor velocity and fail gesture if it is too low gestureLastCheckTime = event.getEventTime(); final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000); int velocityX = (int) velocityTracker.getXVelocity(); int velocityY = (int) velocityTracker.getYVelocity(); // Log.d("Engine_Driver", "gestureVelocity vel " + velocityX); if ((Math.abs(velocityX) < threaded_application.min_fling_velocity) && (Math.abs(velocityY) < threaded_application.min_fling_velocity)) { gestureFailed(event); } } if (gestureInProgress) { // restart the gesture timeout timer mainapp.throttle_msg_handler.postDelayed(gestureStopped, gestureCheckRate); } } } private void gestureEnd(MotionEvent event) { // Log.d("Engine_Driver", "gestureEnd action " + event.getAction() + " inProgress? " + gestureInProgress); mainapp.throttle_msg_handler.removeCallbacks(gestureStopped); if (gestureInProgress) { float deltaX = (event.getX() - gestureStartX); float deltaY = (event.getY() - gestureStartY); float absDeltaX = Math.abs(deltaX); float absDeltaY = Math.abs(deltaY); if ((absDeltaX > threaded_application.min_fling_distance) || (absDeltaY > threaded_application.min_fling_distance)) { // valid gesture. Change the event action to CANCEL so that it isn't processed by any control below the gesture overlay event.setAction(MotionEvent.ACTION_CANCEL); // process swipe in the direction with the largest change if (absDeltaX >= absDeltaY) { // swipe left/right if (!isScreenLocked) { boolean swipeTurnouts = prefs.getBoolean("swipe_through_turnouts_preference", getResources().getBoolean(R.bool.prefSwipeThroughTurnoutsDefaultValue)); swipeTurnouts = swipeTurnouts && mainapp.isTurnoutControlAllowed(); //also check the allowed flag boolean swipeRoutes = prefs.getBoolean("swipe_through_routes_preference", getResources().getBoolean(R.bool.prefSwipeThroughRoutesDefaultValue)); swipeRoutes = swipeRoutes && mainapp.isRouteControlAllowed(); //also check the allowed flag // if swiping (to Turnouts or Routes screen) is enabled, process the swipe if (swipeTurnouts || swipeRoutes) { navigatingAway = true; // left to right swipe goes to turnouts if enabled in prefs if (deltaX > 0.0) { // swipe left Intent in; if (swipeTurnouts) { in = new Intent().setClass(this, turnouts.class); } else { in = new Intent().setClass(this, routes.class); } startActivity(in); connection_activity.overridePendingTransition(this, R.anim.push_right_in, R.anim.push_right_out); } // right to left swipe goes to routes if enabled in prefs else { // swipe right Intent in; if (swipeRoutes) { in = new Intent().setClass(this, routes.class); } else { in = new Intent().setClass(this, turnouts.class); } startActivity(in); connection_activity.overridePendingTransition(this, R.anim.push_left_in, R.anim.push_left_out); } } } } else { // swipe up/down if (deltaY > 0.0) { // swipe down if (!isScreenLocked) { // enter or exit immersive mode only if the preference is set if (immersiveModeIsOn) { setImmersiveModeOff(webView); Toast.makeText(getApplicationContext(), getApplicationContext().getResources() .getString(R.string.toastImmersiveModeDisabled), Toast.LENGTH_SHORT).show(); } else { setImmersiveModeOn(webView); } } } else { // swipe up switch (prefSwipeUpOption) { case SWIPE_UP_OPTION_WEB: showHideWebView(getApplicationContext().getResources() .getString(R.string.toastSwipeUpViewHidden)); break; case SWIPE_UP_OPTION_LOCK: setRestoreScreenLockDim(getApplicationContext().getResources() .getString(R.string.toastSwipeUpScreenLocked)); break; case SWIPE_UP_OPTION_DIM: setRestoreScreenDim(getApplicationContext().getResources() .getString(R.string.toastSwipeUpScreenDimmed)); break; case SWIPE_UP_OPTION_IMMERSIVE: if (immersiveModeIsOn) { setImmersiveModeOff(webView); Toast.makeText(getApplicationContext(), getApplicationContext().getResources() .getString(R.string.toastImmersiveModeDisabled), Toast.LENGTH_SHORT).show(); } else { setImmersiveModeOn(webView); } break; } } } } else { // gesture was not long enough gestureFailed(event); } } } private void gestureCancel(MotionEvent event) { if (mainapp.throttle_msg_handler != null) mainapp.throttle_msg_handler.removeCallbacks(gestureStopped); gestureInProgress = false; gestureFailed = true; } void gestureFailed(MotionEvent event) { // end the gesture gestureInProgress = false; gestureFailed = true; } // // GestureStopped runs when more than gestureCheckRate milliseconds // elapse between onGesture events (i.e. press without movement). // @SuppressLint("Recycle") private Runnable gestureStopped = new Runnable() { @Override public void run() { if (gestureInProgress) { // end the gesture gestureInProgress = false; gestureFailed = true; // create a MOVE event to trigger the underlying control if (vThrotScr != null) { // use uptimeMillis() rather than 0 for time in // MotionEvent.obtain() call in throttle gestureStopped: MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, gestureStartX, gestureStartY, 0); try { vThrotScr.dispatchTouchEvent(event); } catch (IllegalArgumentException e) { Log.d("Engine_Driver", "gestureStopped trigger IllegalArgumentException, OS " + android.os.Build.VERSION.SDK_INT); } } } } }; // helper app to initialize statics (in case GC has not run since app last // shutdown) // call before instantiating any instances of class public static void initStatics() { scale = initialScale; clearHistory = false; currentUrl = null; firstUrl = null; } // prompt for Steal? Address, if yes, send message to execute the steal // public void promptForSteal(String addr, char whichThrottle) { public void promptForSteal(final String addr, final int whichThrottle) { if (stealPromptActive) return; stealPromptActive = true; final AlertDialog.Builder b = new AlertDialog.Builder(this); b.setIcon(android.R.drawable.ic_dialog_alert); b.setTitle(R.string.steal_title); b.setMessage(getString(R.string.steal_text, addr)); b.setCancelable(true); b.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { //if yes pressed, tell ta to proceed with steal public void onClick(DialogInterface dialog, int id) { mainapp.sendMsg(mainapp.comm_msg_handler, message_type.STEAL, addr, whichThrottle); stealPromptActive = false; } }); b.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { //if no pressed do nothing public void onClick(DialogInterface dialog, int id) { stealPromptActive = false; } }); AlertDialog alert = b.create(); alert.show(); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(LocaleHelper.onAttach(base)); } protected void setThottleNumLimits() { if (mainapp.numThrottles > mainapp.maxThrottlesCurrentScreen) { // Maximum number of throttles this screen supports mainapp.numThrottles = mainapp.maxThrottlesCurrentScreen; } if (mainapp.maxThrottles > mainapp.maxThrottlesCurrentScreen) { // Maximum number of throttles this screen supports mainapp.maxThrottles = mainapp.maxThrottlesCurrentScreen; } } private void showTimerPasswordDialog() { AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle(getApplicationContext().getResources().getString(R.string.timerDialogTitle)); alert.setMessage(getApplicationContext().getResources().getString(R.string.timerDialogMessage)); // Set an EditText view to get user input final EditText input = new EditText(this); input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); input.setHint(getApplicationContext().getResources().getString(R.string.timerDialogHint)); alert.setView(input); alert.setPositiveButton(getApplicationContext().getResources().getString(R.string.ok), new DialogInterface.OnClickListener() { @SuppressLint("ApplySharedPref") public void onClick(DialogInterface dialog, int whichButton) { passwordText = input.getText().toString(); Log.d("", "Password Value : " + passwordText); if (passwordText.equals(prefKidsTimerResetPassword)) { //reset kidsTimerActions(KIDS_TIMER_ENDED, 0); kidsTimerActions(KIDS_TIMER_DISABLED, 0); prefs.edit().putString("prefKidsTimer", PREF_KIDS_TIMER_NONE).commit(); //reset the preference getKidsTimerPrefs(); } if (passwordText.equals(prefKidsTimerRestartPassword)) { //reset kidsTimerActions(KIDS_TIMER_ENABLED, 0); } // return; } }); alert.setNegativeButton(getApplicationContext().getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // return; } }); alert.show(); } @SuppressLint("SwitchIntDef") public void navigateToHandler(@PermissionsHelper.RequestCodes int requestCode) { if (!PermissionsHelper.getInstance().isPermissionGranted(throttle.this, requestCode)) { PermissionsHelper.getInstance().requestNecessaryPermissions(throttle.this, requestCode); } else { // Go to the correct handler based on the request code. // Only need to consider relevant request codes initiated by this Activity switch (requestCode) { case PermissionsHelper.READ_SERVER_AUTO_PREFERENCES: Log.d("Engine_Driver", "Got permission for STORE_PREFERENCES - navigate to saveSharedPreferencesToFileImpl()"); autoImportUrlAskToImportImpl(); break; case PermissionsHelper.STORE_SERVER_AUTO_PREFERENCES: Log.d("Engine_Driver", "Got permission for STORE_PREFERENCES - navigate to saveSharedPreferencesToFileImpl()"); autoImportFromURLImpl(); break; default: // do nothing Log.d("Engine_Driver", "Unrecognised permissions request code: " + requestCode); } } } private void autoImportFromURL() { navigateToHandler(PermissionsHelper.STORE_SERVER_AUTO_PREFERENCES); } public void autoImportFromURLImpl() { new autoImportFromURL().execute(); } public class autoImportFromURL extends AsyncTask<String, String, String> { // Background Async Task to download file // Importing file in background thread @SuppressLint("ApplySharedPref") @Override protected String doInBackground(String... f_url) { Log.d("Engine_Driver", "throttle: Import preferences from Server: start"); int count; String n_url; if ((mainapp.connectedHostip != null)) { n_url = "http://" + mainapp.connectedHostip + ":" + mainapp.web_server_port + "/prefs/" + ENGINE_DRIVER_DIR + "/" + EXTERNAL_PREFERENCES_IMPORT_FILENAME; } else { // not currently connected return null; } String urlPreferencesFileName; String urlPreferencesFilePath; URLConnection connection; URL url; try { urlPreferencesFileName = "auto_" + mainapp.connectedHostName.replaceAll("[^A-Za-z0-9_]", "_") + ".ed"; urlPreferencesFilePath = Environment.getExternalStorageDirectory().toString() + "/" + ENGINE_DRIVER_DIR + "/" + urlPreferencesFileName; url = new URL(n_url); connection = url.openConnection(); connection.connect(); } catch (Exception e) { Log.d("Engine_Driver", "throttle: Auto import preferences from Server Failed: " + e.getMessage()); return null; } try { Date urlDate = new Date(connection.getLastModified()); // need to compare the dates here File localFile = new File(urlPreferencesFilePath); Date localDate; if (localFile.exists()) { long timestamp = localFile.lastModified(); localDate = new Date(timestamp); if (localDate.compareTo(urlDate) >= 0) { Log.d("Engine_Driver", "throttle: Auto Import preferences from Server: Local file is up-to-date"); return null; // } else { // Log.d("Engine_Driver", "throttle: Import preferences from Server: Local file is newer. Date " + localDate.toString()); } } // download the file InputStream input = new BufferedInputStream(url.openStream(), 8192); File Directory = new File(ENGINE_DRIVER_DIR); // in case the folder does not already exist FileOutputStream output = new FileOutputStream(urlPreferencesFilePath); byte data[] = new byte[1024]; while ((count = input.read(data)) != -1) { output.write(data, 0, count); } output.flush(); output.close(); input.close(); prefs.edit().putString("prefPreferencesImportFileName", urlPreferencesFilePath).commit(); mainapp.sendMsg(mainapp.comm_msg_handler, message_type.IMPORT_SERVER_AUTO_AVAILABLE, "", 0); } catch (Exception e) { Log.e("Engine_Driver", "throttle: Auto import preferences from Server Failed: " + e.getMessage()); } Log.d("Engine_Driver", "throttle: Auto Import preferences from Server: End"); return null; } } private void autoImportUrlAskToImport() { navigateToHandler(PermissionsHelper.READ_SERVER_AUTO_PREFERENCES); } private void autoImportUrlAskToImportImpl() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { //@Override @SuppressLint("ApplySharedPref") public void onClick(DialogInterface dialog, int which) { String deviceId = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID); String urlPreferencesFileName = "auto_" + mainapp.connectedHostName.replaceAll("[^A-Za-z0-9_]", "_") + ".ed"; switch (which) { case DialogInterface.BUTTON_POSITIVE: prefs.edit().putString("prefPreferencesImportAll", PREF_IMPORT_ALL_FULL).commit(); loadSharedPreferencesFromFile(prefs, urlPreferencesFileName, deviceId, FORCED_RESTART_REASON_IMPORT_SERVER_AUTO); break; case DialogInterface.BUTTON_NEUTRAL: prefs.edit().putString("prefPreferencesImportAll", PREF_IMPORT_ALL_PARTIAL).commit(); loadSharedPreferencesFromFile(prefs, urlPreferencesFileName, deviceId, FORCED_RESTART_REASON_IMPORT_SERVER_AUTO); break; case DialogInterface.BUTTON_NEGATIVE: prefs.edit().putString("prefPreferencesImportAll", PREF_IMPORT_ALL_RESET).commit(); break; } } }; AlertDialog.Builder ab = new AlertDialog.Builder(throttle.this); ab.setMessage(R.string.importServerAutoDialog) .setPositiveButton(R.string.importServerAutoDialogPositiveButton, dialogClickListener) .setNegativeButton(R.string.no, dialogClickListener) .setNeutralButton(R.string.importServerAutoDialogNeutralButton, dialogClickListener); ab.show(); } @SuppressWarnings({ "unchecked" }) private void loadSharedPreferencesFromFile(SharedPreferences sharedPreferences, String exportedPreferencesFileName, String deviceId, int forceRestartReason) { Log.d("Engine_Driver", "Preferences: Loading saved preferences from file: " + exportedPreferencesFileName); boolean res = importExportPreferences.loadSharedPreferencesFromFile(getApplicationContext(), sharedPreferences, exportedPreferencesFileName, deviceId); if (!res) { Toast.makeText( getApplicationContext(), getApplicationContext().getResources() .getString(R.string.prefImportExportErrorReadingFrom, exportedPreferencesFileName), Toast.LENGTH_LONG).show(); } forceRestartApp(forceRestartReason); } @SuppressLint("ApplySharedPref") public void forceRestartApp(int forcedRestartReason) { Log.d("Engine_Driver", "throttle.forceRestartApp() "); SharedPreferences sharedPreferences = getSharedPreferences("jmri.enginedriver_preferences", 0); sharedPreferences.edit().putBoolean("prefForcedRestart", false).commit(); sharedPreferences.edit().putInt("prefForcedRestartReason", forcedRestartReason).commit(); Intent intent = getBaseContext().getPackageManager() .getLaunchIntentForPackage(getBaseContext().getPackageName()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish(); Runtime.getRuntime().exit(0); // really force the kill } }