/*
* The MIT License (MIT)
*
* Copyright (c) 2014 Curt Binder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package info.curtbinder.reefangel.phone;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import info.curtbinder.reefangel.controller.Controller;
import info.curtbinder.reefangel.db.StatusProvider;
import info.curtbinder.reefangel.db.StatusTable;
import info.curtbinder.reefangel.service.MessageCommands;
import info.curtbinder.reefangel.service.UpdateService;
import info.curtbinder.reefangel.service.XMLTags;
public class StatusFragment extends Fragment {
private static final String TAG = StatusFragment.class.getSimpleName();
// minimum number of pages: flags, commands, status, main relay
private static final int MIN_PAGES = 4;
private static final int POS_START = 0;
private static final int POS_FLAGS = POS_START;
private static final int POS_COMMANDS = POS_START + 1;
private static final int POS_CONTROLLER = POS_START + 2;
// add on module pages
private static final int POS_MODULES = POS_CONTROLLER + 10;
private static final int POS_DIMMING = POS_MODULES;
private static final int POS_SC_DIMMING = POS_MODULES + 1;
private static final int POS_RADION = POS_MODULES + 2;
private static final int POS_VORTECH = POS_MODULES + 3;
private static final int POS_DCPUMP = POS_MODULES + 4;
private static final int POS_AI = POS_MODULES + 5;
private static final int POS_IO = POS_MODULES + 6;
private static final int POS_CUSTOM = POS_MODULES + 7;
// relay pages
private static final int POS_MAIN_RELAY = POS_CONTROLLER + 1;
private static final int POS_EXP1_RELAY = POS_MAIN_RELAY + 1;
private static final int POS_EXP2_RELAY = POS_MAIN_RELAY + 2;
private static final int POS_EXP3_RELAY = POS_MAIN_RELAY + 3;
private static final int POS_EXP4_RELAY = POS_MAIN_RELAY + 4;
private static final int POS_EXP5_RELAY = POS_MAIN_RELAY + 5;
private static final int POS_EXP6_RELAY = POS_MAIN_RELAY + 6;
private static final int POS_EXP7_RELAY = POS_MAIN_RELAY + 7;
private static final int POS_EXP8_RELAY = POS_MAIN_RELAY + 8;
private static final int POS_END = POS_CUSTOM + 1;
private static final String CURRENT_POSITION = "currentPosition";
private static int currentPosition = POS_CONTROLLER;
// Message Receivers
StatusReceiver receiver;
IntentFilter filter;
// display views
private TextView mUpdateTime;
private ViewPager mPager;
private SectionsPagerAdapter mPagerAdapter;
private Fragment[] mAppPages;
private int[] mAppPageOrder;
private String[] mAppPageTitles;
private RAApplication raApp;
private boolean fReloadPages = false;
public static StatusFragment newInstance() {
return new StatusFragment();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mPager != null) {
currentPosition = mPager.getCurrentItem();
outState.putInt(CURRENT_POSITION, currentPosition);
}
}
@Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
if (savedInstanceState != null) {
currentPosition = savedInstanceState.getInt(CURRENT_POSITION, POS_CONTROLLER);
}
Log.d(TAG, "onViewStateRestored: pos: " + currentPosition);
}
@Override
public View onCreateView(
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
raApp = (RAApplication) getActivity().getApplication();
View root = inflater.inflate(R.layout.frag_status, container, false);
createMessageReceiver();
mUpdateTime = (TextView) root.findViewById(R.id.textUpdate);
mAppPageOrder = new int[POS_END];
createPages();
updatePageOrder();
// Set up the ViewPager with the sections adapter.
mPager = (ViewPager) root.findViewById(R.id.pager);
/* we are using Nested Fragments inside the pager adapter to allow the
* fragment manager to manage them properly, instead of using the
* getSupportFragmentManager or getFragmentManager, you must use
* getChildFragmentManager to allow for proper management
*/
mPagerAdapter = new SectionsPagerAdapter(getChildFragmentManager());
mPager.setAdapter(mPagerAdapter);
mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// example used mPagerAdapter.instantiateItem(mPager, position)
refreshPageData(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
// enable the options menu
setHasOptionsMenu(true);
return root;
}
private void createPages() {
// set the maximum number of pages we can have
mAppPages = new Fragment[POS_END];
mAppPageTitles = new String[POS_END];
mAppPages[POS_FLAGS] = PageFlagsFragment.newInstance();
mAppPageTitles[POS_FLAGS] = getString(R.string.titleFlags);
mAppPages[POS_COMMANDS] = PageCommandsFragment.newInstance();
mAppPageTitles[POS_COMMANDS] = getString(R.string.titleCommands);
mAppPages[POS_CONTROLLER] = PageControllerFragment.newInstance();
mAppPageTitles[POS_CONTROLLER] = getString(R.string.labelController);
mAppPages[POS_DIMMING] = PageDimmingFragment.newInstance();
mAppPageTitles[POS_DIMMING] = getString(R.string.labelDimming);
mAppPages[POS_SC_DIMMING] = PageSCDimmingFragment.newInstance();
mAppPageTitles[POS_SC_DIMMING] = getString(R.string.labelSCDimming);
mAppPages[POS_RADION] = PageRadionFragment.newInstance();
mAppPageTitles[POS_RADION] = getString(R.string.labelRadion);
mAppPages[POS_VORTECH] = PageVortechFragment.newInstance();
mAppPageTitles[POS_VORTECH] = getString(R.string.labelVortech);
mAppPages[POS_DCPUMP] = PageDCPumpFragment.newInstance();
mAppPageTitles[POS_DCPUMP] = getString(R.string.labelDCPump);
mAppPages[POS_AI] = PageAIFragment.newInstance();
mAppPageTitles[POS_AI] = getString(R.string.labelAI);
mAppPages[POS_IO] = PageIOFragment.newInstance();
mAppPageTitles[POS_IO] = getString(R.string.labelIO);
mAppPages[POS_CUSTOM] = PageCustomFragment.newInstance();
mAppPageTitles[POS_CUSTOM] = getString(R.string.labelCustomVariables);
for (int i = POS_MAIN_RELAY, j = 0; i < POS_EXP8_RELAY + 1; i++, j++) {
// should be 3 through 11
mAppPages[i] = PageRelayFragment.newInstance(j);
mAppPageTitles[i] = getRelayPageTitle(j);
}
}
private void redrawPages() {
updatePageOrder();
mPagerAdapter.notifyDataSetChanged();
fReloadPages = false;
}
public void reloadPages() {
// called by parent activity when entering settings
// scroll to first page on entering settings
mPager.setCurrentItem(POS_CONTROLLER, false);
// force the pages to be redrawn if we enter the settings
fReloadPages = true;
}
private void updatePageOrder() {
int page, pos;
int qty = raApp.raprefs.getExpansionRelayQuantity();
// loop through all the possible pages
// keep track of the pages installed compared to the total pages
// if the module is enabled, add it to the available pages list
// then increment the installed pages counter
for (page = POS_START, pos = POS_START; page <= POS_END; page++) {
switch (page) {
// the first 4 pages are non-changeable, each case performs
// the same action
case POS_FLAGS:
case POS_COMMANDS:
case POS_CONTROLLER:
case POS_MAIN_RELAY:
mAppPageOrder[pos] = page;
pos++;
break;
case POS_DIMMING:
if (raApp.raprefs.getDimmingModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_SC_DIMMING:
if (raApp.raprefs.getSCDimmingModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_RADION:
if (raApp.raprefs.getRadionModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_VORTECH:
if (raApp.raprefs.getVortechModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_DCPUMP:
if (raApp.raprefs.getDCPumpModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_AI:
if (raApp.raprefs.getAIModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_IO:
if (raApp.raprefs.getIOModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_CUSTOM:
if (raApp.raprefs.getCustomModuleEnabled()) {
mAppPageOrder[pos] = page;
pos++;
}
break;
case POS_EXP1_RELAY:
case POS_EXP2_RELAY:
case POS_EXP3_RELAY:
case POS_EXP4_RELAY:
case POS_EXP5_RELAY:
case POS_EXP6_RELAY:
case POS_EXP7_RELAY:
case POS_EXP8_RELAY:
if (qty > 0) {
int relay = page - POS_EXP1_RELAY;
if (relay < qty) {
mAppPageOrder[pos] = page;
pos++;
}
}
break;
}
}
// fill the rest of the order array with the first position...
// ie, zero out the rest of the array
if (pos < POS_END) {
for (; pos < POS_END; pos++) {
mAppPageOrder[pos] = POS_START;
}
}
}
private String getRelayPageTitle(int relay) {
int id;
switch (relay) {
default:
case 0:
id = R.string.prefMainRelayTitle;
break;
case 1:
id = R.string.prefExp1RelayTitle;
break;
case 2:
id = R.string.prefExp2RelayTitle;
break;
case 3:
id = R.string.prefExp3RelayTitle;
break;
case 4:
id = R.string.prefExp4RelayTitle;
break;
case 5:
id = R.string.prefExp5RelayTitle;
break;
case 6:
id = R.string.prefExp6RelayTitle;
break;
case 7:
id = R.string.prefExp7RelayTitle;
break;
case 8:
id = R.string.prefExp8RelayTitle;
break;
}
return getString(id);
}
private void createMessageReceiver() {
// Message Receiver stuff
receiver = new StatusReceiver();
filter = new IntentFilter(MessageCommands.UPDATE_DISPLAY_DATA_INTENT);
filter.addAction(MessageCommands.UPDATE_STATUS_INTENT);
filter.addAction(MessageCommands.COMMAND_RESPONSE_INTENT);
filter.addAction(MessageCommands.ERROR_MESSAGE_INTENT);
filter.addAction(MessageCommands.VORTECH_POPUP_INTENT);
filter.addAction(MessageCommands.MEMORY_RESPONSE_INTENT);
filter.addAction(MessageCommands.COMMAND_RESPONSE_INTENT);
filter.addAction(MessageCommands.VERSION_RESPONSE_INTENT);
filter.addAction(MessageCommands.OVERRIDE_RESPONSE_INTENT);
filter.addAction(MessageCommands.OVERRIDE_POPUP_INTENT);
filter.addAction(MessageCommands.CUSTOMVAR_RESPONSE_INTENT);
filter.addAction(MessageCommands.CALIBRATE_RESPONSE_INTENT);
}
private void refreshPageData(int position) {
PageRefreshInterface page = (PageRefreshInterface) mPagerAdapter.getItem(position);
if (page != null) {
page.refreshData();
}
}
private void checkDeviceModules() {
Cursor c = getLatestDataCursor();
short newEM, newEM1, newREM;
if (c.moveToFirst()) {
newEM = c.getShort(c.getColumnIndex(StatusTable.COL_EM));
newEM1 = c.getShort(c.getColumnIndex(StatusTable.COL_EM1));
newREM = c.getShort(c.getColumnIndex(StatusTable.COL_REM));
} else {
newEM = newEM1 = newREM = 0;
}
c.close();
if (checkExpansionModules(newEM) ||
checkExpansionModules1(newEM1) ||
checkRelayModules(newREM)) {
// TODO do we call updateViewsAndVisibility??
// if the modules change, redraw the pages first then navigate to the main page
redrawPages();
reloadPages();
}
}
private boolean checkExpansionModules(short newEM) {
boolean fReload = false;
short oldEM = (short) raApp.raprefs.getPreviousEM();
Log.d(TAG, "EM: Old: " + oldEM + " New: " + newEM);
if (oldEM != newEM) {
// expansion modules are different
// set the flag to reload the pages
fReload = true;
// check which expansion modules are installed
// set the installed modules in the preferences
boolean f;
f = (Controller.isAIModuleInstalled(newEM));
Log.d(TAG, "AI: " + f);
raApp.raprefs.set(R.string.prefExpAIEnableKey, f);
f = (Controller.isDimmingModuleInstalled(newEM));
Log.d(TAG, "Dimming: " + f);
raApp.raprefs.set(R.string.prefExpDimmingEnableKey, f);
f = (Controller.isIOModuleInstalled(newEM));
Log.d(TAG, "IO: " + f);
raApp.raprefs.set(R.string.prefExpIOEnableKey, f);
f = (Controller.isORPModuleInstalled(newEM));
Log.d(TAG, "ORP: " + f);
raApp.raprefs.set(R.string.prefORPVisibilityKey, f);
f = (Controller.isPHExpansionModuleInstalled(newEM));
Log.d(TAG, "PHE: " + f);
raApp.raprefs.set(R.string.prefPHExpVisibilityKey, f);
f = (Controller.isRFModuleInstalled(newEM));
Log.d(TAG, "RF: " + f);
raApp.raprefs.set(R.string.prefExpRadionEnableKey, f);
raApp.raprefs.set(R.string.prefExpVortechEnableKey, f);
f = (Controller.isSalinityModuleInstalled(newEM));
Log.d(TAG, "Salinity: " + f);
raApp.raprefs.set(R.string.prefSalinityVisibilityKey, f);
f = (Controller.isWaterLevelModuleInstalled(newEM));
Log.d(TAG, "Water: " + f);
String key;
for (int i = 0; i < Controller.MAX_WATERLEVEL_PORTS; i++) {
key = "wl";
if (i > 0) key += i;
key += "_visibility";
raApp.raprefs.set(key, f);
}
// update the previous settings to the new ones after we change
raApp.raprefs.setPreviousEM(newEM);
}
return fReload;
}
private boolean checkExpansionModules1(short newEM1) {
boolean fReload = false;
short oldEM1 = (short) raApp.raprefs.getPreviousEM1();
Log.d(TAG, "EM1: Old: " + oldEM1 + " New: " + newEM1);
if (oldEM1 != newEM1) {
fReload = true;
boolean f;
f = (Controller.isHumidityModuleInstalled(newEM1));
Log.d(TAG, "Humidity: " + f);
raApp.raprefs.set(R.string.prefHumidityVisibilityKey, f);
f = (Controller.isDCPumpControlModuleInstalled(newEM1));
Log.d(TAG, "DCPump: " + f);
raApp.raprefs.set(R.string.prefExpDCPumpEnabledKey, f);
f = (Controller.isLeakDetectorModuleInstalled(newEM1));
Log.d(TAG, "Leak: " + f);
// TODO enable Leak module?? maybe it's not needed since it's a flag
//raApp.raprefs.set(R.string.prefExpLeakDetectorEnableKey, f);
raApp.raprefs.setPreviousEM1(newEM1);
}
return fReload;
}
private boolean checkRelayModules(short newREM) {
boolean fReload = false;
int oldRQty = raApp.raprefs.getExpansionRelayQuantity();
int newRQty = Controller.getRelayExpansionModulesInstalled(newREM);
Log.d(TAG, "Old Qty: " + oldRQty + " New Qty: " + newRQty);
if (oldRQty != newRQty) {
fReload = true;
Log.d(TAG, "Relays: " + newRQty);
raApp.raprefs.set(R.string.prefExpQtyKey, Integer.toString(newRQty));
}
return fReload;
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
getActivity().registerReceiver(receiver, filter, Permissions.QUERY_STATUS, null);
getActivity().registerReceiver(receiver, filter, Permissions.SEND_COMMAND, null);
// Redraw pages if needed
if (fReloadPages) {
redrawPages();
}
mPager.setCurrentItem(currentPosition, false);
}
public void updateDisplayText(String text) {
mUpdateTime.setText(text);
}
public String[] getNeverValues(int qty) {
String[] s = new String[qty];
for (int i = 0; i < qty; i++) {
s[i] = getString(R.string.defaultStatusText);
}
return s;
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
getActivity().unregisterReceiver(receiver);
}
private void launchStatusTask() {
Intent i = new Intent(getActivity(), UpdateService.class);
i.setAction(MessageCommands.QUERY_STATUS_INTENT);
getActivity().startService(i);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.frag_status, menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
Log.d(TAG, "Refresh Data");
launchStatusTask();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void displayOverrideDialog(int channel, short value) {
DialogOverridePwm d = DialogOverridePwm.newInstance(channel, value,
raApp.getPWMOverrideMessageDisplay(channel));
d.show(getFragmentManager(), "dlgoverridepwm");
}
public void displayVortechDialog(int type, int value) {
DialogVortech d = DialogVortech.newInstance(type, value,
raApp.raprefs.useOldPre10MemoryLocations());
d.show(getFragmentManager(), "dlgvortech");
}
public void displayDCPumpDialog(int type, int value) {
DialogDCPump d= DialogDCPump.newInstance(type, value);
d.show(getFragmentManager(), "dlgdcpump");
}
public void displayCustomVarDialog(int channel, short value, String msg) {
DialogCustomVar d = DialogCustomVar.newInstance(channel, value, msg);
d.show(getFragmentManager(), "dlgcustomvar");
}
protected Cursor getLatestDataCursor() {
Uri uri = Uri.parse(StatusProvider.CONTENT_URI + "/" + StatusProvider.PATH_LATEST);
return getActivity().getContentResolver().query(uri, null, null, null,
StatusTable.COL_ID + " DESC");
}
private class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return mAppPages[mAppPageOrder[position]];
}
@Override
public int getCount() {
return MIN_PAGES + raApp.raprefs.getTotalInstalledModuleQuantity();
}
@Override
public CharSequence getPageTitle(int position) {
// this gets called before the getItem function gets called
return mAppPageTitles[mAppPageOrder[position]];
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
class StatusReceiver extends BroadcastReceiver {
// private final String TAG = StatusReceiver.class.getSimpleName();
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(MessageCommands.UPDATE_STATUS_INTENT)) {
int id = intent.getIntExtra(MessageCommands.UPDATE_STATUS_ID, R.string.defaultStatusText);
if (id > -1) {
mUpdateTime.setText(id);
} else {
// we are updating with a string being sent to us
mUpdateTime.setText(intent
.getStringExtra(MessageCommands.UPDATE_STATUS_STRING));
}
} else if (action.equals(MessageCommands.UPDATE_DISPLAY_DATA_INTENT)) {
// get the current fragment
// call it's update data function
refreshPageData(mPager.getCurrentItem());
// only refresh after we get the command to refresh
// otherwise, if we have this check inside the refreshPageData function,
// we have this check being called every time the pages are changed when
// the fragment is updating its data
if (raApp.raprefs.isAutoUpdateModulesEnabled()) {
// update the screen / pages if necessary
checkDeviceModules();
}
} else if (action.equals(MessageCommands.MEMORY_RESPONSE_INTENT)) {
// for vortech responses
String response = intent.getStringExtra(MessageCommands.MEMORY_RESPONSE_STRING);
displayResponse(response, -1, false);
} else if (action.equals(MessageCommands.OVERRIDE_RESPONSE_INTENT)) {
String response = intent.getStringExtra(MessageCommands.OVERRIDE_RESPONSE_STRING);
displayResponse(response, -1, false);
} else if (action.equals(MessageCommands.CUSTOMVAR_RESPONSE_INTENT)) {
String response = intent.getStringExtra(MessageCommands.CUSTOMVAR_RESPONSE_STRING);
displayResponse(response, -1, false);
} else if (action.equals(MessageCommands.COMMAND_RESPONSE_INTENT)) {
String response = intent.getStringExtra(MessageCommands.COMMAND_RESPONSE_STRING);
displayResponse(response, -1, false);
} else if (action.equals(MessageCommands.CALIBRATE_RESPONSE_INTENT)) {
String response =
intent.getStringExtra(MessageCommands.CALIBRATE_RESPONSE_STRING);
displayResponse(response, R.string.statusFinished, true);
} else if (action.equals(MessageCommands.VERSION_RESPONSE_INTENT)) {
// set the version button's text to the version of the software
((PageCommandsFragment) mAppPages[POS_COMMANDS]).setButtonVersion(
intent.getStringExtra(MessageCommands.VERSION_RESPONSE_STRING)
);
mUpdateTime.setText(R.string.statusFinished);
}
}
}
private void displayResponse(String response, int stringId, boolean fAlwaysToast) {
int msgId;
boolean fShowToast = false;
if (stringId == -1) {
msgId = R.string.statusRefreshNeeded;
} else {
msgId = stringId;
}
if (response.contains(XMLTags.Ok)) {
mUpdateTime.setText(msgId);
if (raApp.raprefs.isAutoRefreshAfterUpdate()) {
mUpdateTime.setText(R.string.statusWaiting);
Log.d(TAG, "AutoRefreshAfterUpdate");
Handler h = new Handler();
Runnable r = new Runnable() {
public void run() {
launchStatusTask();
}
};
// pause for a second before we proceed
h.postDelayed(r, 1000);
}
} else {
fShowToast = true;
}
if (fAlwaysToast || fShowToast) {
Toast.makeText(getActivity(), response, Toast.LENGTH_LONG).show();
}
}
}