Java tutorial
/* * Copyright (c) 2012, Robert von Burg * * All rights reserved. * * This file is part of the XXX. * * XXX 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. * * XXX 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 XXX. If not, see * <http://www.gnu.org/licenses/>. */ package ch.eitchnet.android.mabea.activity; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.joda.time.LocalDateTime; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.ToggleButton; import ch.eitchnet.android.mabea.MabeaApplication; import ch.eitchnet.android.mabea.MabeaConnection; import ch.eitchnet.android.mabea.R; import ch.eitchnet.android.mabea.model.Booking; import ch.eitchnet.android.mabea.model.MabeaContext; import ch.eitchnet.android.mabea.model.MabeaState; import ch.eitchnet.android.mabea.model.MabeaState.State; import ch.eitchnet.android.mabea.model.Today; import ch.eitchnet.android.util.DialogUtil; import ch.eitchnet.android.util.JodaHelper; import ch.eitchnet.android.util.Logger; import ch.eitchnet.android.util.SyncExec; /** * @author Robert von Burg <eitch@eitchnet.ch> * */ public class TodayFragment extends Fragment { private static final String TAG = TodayFragment.class.getSimpleName(); private View rootView; private ToggleButton toggleStateBtn; private ProgressBar progressBar; private ProgressDialog progressDlg; private ListView bookingsToday; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Logger.i(TAG, "onCreateView", "called"); this.rootView = inflater.inflate(R.layout.fragment_today_layout, container, false); this.progressBar = (ProgressBar) this.rootView.findViewById(R.id.progressBar); this.progressBar.setVisibility(View.INVISIBLE); this.toggleStateBtn = (ToggleButton) this.rootView.findViewById(R.id.toggleLoggedIn); this.bookingsToday = (ListView) this.rootView.findViewById(R.id.bookingsToday); List<? extends Map<String, ?>> data = new ArrayList<Map<String, ?>>(); int resource = R.layout.list_item_booking_today; String[] from = new String[] {}; int[] to = new int[] {}; SimpleAdapter adapter = new SimpleAdapter(getActivity(), data, resource, from, to); this.bookingsToday.setAdapter(adapter); registerObservers(); return this.rootView; } @Override public void onPause() { Logger.i(TAG, "onPause", "Pausing."); super.onPause(); } @Override public void onResume() { updateUi(); Logger.i(TAG, "onPause", "Resumed."); super.onResume(); } private void registerObservers() { this.toggleStateBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TodayFragment.this.progressBar.setVisibility(View.VISIBLE); new Thread(new Runnable() { @Override public void run() { toggleLoginState(); } }, "ToggleState").start(); } }); } private void toggleLoginState() { try { // validate the context MabeaContext context = MabeaApplication.getContext(); if (!context.isInitialized()) { String title = "State change failed"; String msg = "The settings have not yet been validated, please use the settings tab to configure and validate the settings."; DialogUtil.showErrorDialog(getActivity(), title, msg); updateUiOnUiThread(); return; } // first see what state we expect // but since the UI button was pressed and thus toggled, we must negate current UI MabeaState.State expectedState = !toggleStateBtn.isChecked() ? State.LOGGED_IN : State.LOGGED_OUT; // get the connection and see if we need to refresh the state MabeaConnection connection = context.getConnection(); if (!checkConnection()) { Logger.i(TAG, "toggleLoginState", "refresh failed as connection is not ok. Stopping"); updateUiOnUiThread(); return; } // check that we don't have a state mismatch MabeaState currentMabeaState = connection.getMabeaState(); if (!handleCurrentState(expectedState, currentMabeaState)) { Logger.i(TAG, "toggleLoginState", "It seems the current server state is invalid and not recoverable. Stopping"); updateUiOnUiThread(); return; } // now toggle the actual state State currentState = currentMabeaState.getState(); if (currentState == State.LOGGED_OUT) { login(); } else if (currentState == State.LOGGED_IN) { logout(); } else { throw new IllegalStateException("Current state is " + currentState + " can't toggle that!"); } updateUiOnUiThread(); } catch (Exception e) { Logger.e(TAG, "toggleLoginState", e.getMessage(), e); DialogUtil.showExceptionDialog(getActivity(), TAG, e); if (progressDlg != null) progressDlg.dismiss(); updateUiOnUiThread(); } } /** * @param expectedState * @param currentState * @return */ private boolean handleCurrentState(State expectedState, MabeaState currentState) { // if the state is as expected, then all is good if (expectedState.equals(currentState.getState())) { return true; } // we have a mismatch. We can recover from it, if the time stamp is // later than what we have currently registered // so check if the server state is later than the current booking Today today = MabeaApplication.getContext().getToday(); Booking latestBooking = today.getLatestBooking(); LocalDateTime expectedDate = latestBooking.getTimestamp(); LocalDateTime currentDate = currentState.getStateTime(); if (expectedDate.isAfter(currentDate) && currentDate.isBefore(LocalDateTime.now())) { String title = "State mismatch"; String msg = "The state on the server is {0} which is not the expected state {1}.\n\nThe local state can't be toggled as the local timestamp {2} is after the new state from the server. Please check your expected state."; msg = MessageFormat.format(msg, currentState.getState(), expectedState, JodaHelper.toDateHourMinute(expectedDate), JodaHelper.toDateHourMinute(currentDate)); DialogUtil.showErrorDialog(getActivity(), title, msg); return false; } // fix state mismatch by adding a new booking Booking booking = new Booking(currentState.getState(), currentDate, currentState.getBalance()); today.addBooking(booking); // notify user of fix String title = "State mismatch"; String msg = "The state on the server is {0} which is not the expected state {1}.\n\nAs the time stamp of the current state is later than the latest booking, a new booking was added to today. Please check your expected state."; msg = MessageFormat.format(msg, currentState.getState(), expectedState, JodaHelper.toDateHourMinute(expectedDate), JodaHelper.toDateHourMinute(currentDate)); DialogUtil.showErrorDialog(getActivity(), title, msg); return true; } private boolean checkConnection() { MabeaContext context = MabeaApplication.getContext(); MabeaConnection connection = context.getConnection(); if (!connection.needsRefresh()) { return true; } SyncExec action = new SyncExec() { @Override protected void execute() { String title = "Refreshing connection"; String msg = "Connection needs to be refreshed, please wait..."; progressDlg = ProgressDialog.show(getActivity(), title, msg); } }; action.runOnUiThread(getActivity()); Logger.i(TAG, "toggleLoginState", "refreshing connection..."); State state = connection.checkConnection(); if (state == State.UNKNOWN) { String title = "Toggling login state failed"; String msg = "Connection could not be verified due to error: " + connection.getErrorMsg(); DialogUtil.showErrorDialog(getActivity(), title, msg); } if (progressDlg != null) progressDlg.dismiss(); return !connection.hasError(); } private void login() { MabeaContext context = MabeaApplication.getContext(); MabeaConnection connection = context.getConnection(); State state = connection.login(); if (state == State.LOGGED_IN) { MabeaState mabeaState = connection.getMabeaState(); Booking newBooking = new Booking(mabeaState.getState(), mabeaState.getStateTime(), mabeaState.getBalance()); context.getToday().addBooking(newBooking); } else { String title = "Logging in failed"; String msg = "Failed to login due to error: " + connection.getErrorMsg(); DialogUtil.showErrorDialog(getActivity(), title, msg); } } private void logout() { MabeaContext context = MabeaApplication.getContext(); MabeaConnection connection = context.getConnection(); State state = connection.logout(); if (state == State.LOGGED_OUT) { MabeaState mabeaState = connection.getMabeaState(); context.getToday().addBooking( new Booking(mabeaState.getState(), mabeaState.getStateTime(), mabeaState.getBalance())); } else { String title = "Logging out failed"; String msg = "Failed to logout due to error: " + connection.getErrorMsg(); DialogUtil.showErrorDialog(getActivity(), title, msg); } } public void updateUiOnUiThread() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { updateUi(); } }); } public void updateUi() { if (this.rootView == null) { return; } try { Logger.i(TAG, "updateUi", "Updating UI..."); this.progressBar.setVisibility(View.INVISIBLE); MabeaContext context = MabeaApplication.getContext(); if (!context.isConnectionOk()) { Logger.i(TAG, "updateUi", "Connection not ok. Stopping."); this.toggleStateBtn.setChecked(false); return; } MabeaState connectionState = context.getConnection().getMabeaState(); TextView nameTxt = (TextView) this.rootView.findViewById(R.id.txtName); nameTxt.setText(connectionState.getName() + "(" + context.getSetting().getUsername() + ")"); Today today = context.getToday(); TextView status1Txt = (TextView) this.rootView.findViewById(R.id.txtStatus1); Booking booking = today.getLatestBooking(); String time = JodaHelper.toHourMinute(booking.getTimestamp()); State loginState = booking.getState(); if (loginState == State.LOGGED_IN) { status1Txt.setText("Logged in since " + time + " / Balance: " + JodaHelper.toHourMinute(today.getActualBalance())); this.toggleStateBtn.setChecked(true); } else if (loginState == State.LOGGED_OUT) { status1Txt.setText("Logged out since " + time + " / Balance: " + JodaHelper.toHourMinute(today.getActualBalance())); this.toggleStateBtn.setChecked(false); } else { status1Txt.setText("Unknown state since " + time + " / Balance: " + JodaHelper.toHourMinute(today.getActualBalance())); this.toggleStateBtn.setChecked(false); } TextView status2Txt = (TextView) this.rootView.findViewById(R.id.txtStatus2); status2Txt.setText("Balance: " + JodaHelper.toHourMinute(today.getEstimatedBalance()) + " / Work: " + JodaHelper.toHourMinute(today.getWorkToday()) + " (" + JodaHelper.toHourMinute(today.getRemainingWork()) + ")"); } catch (Exception e) { DialogUtil.showExceptionDialog(getActivity(), "Update UI failed", e); this.progressBar.setVisibility(View.INVISIBLE); } } }