package com.hitherejoe.watchtower.ui.fragment; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v4.app.Fragment; 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.EditText; import android.widget.Spinner; import android.widget.TextView; import com.hitherejoe.watchtower.R; import com.hitherejoe.watchtower.WatchTowerApplication; import com.hitherejoe.watchtower.data.BusEvent; import com.hitherejoe.watchtower.data.DataManager; import com.hitherejoe.watchtower.data.model.AdvertisedId; import com.hitherejoe.watchtower.data.model.Beacon; import com.hitherejoe.watchtower.data.model.Beacon.Status; import com.hitherejoe.watchtower.data.model.Beacon.Stability; import com.hitherejoe.watchtower.data.model.LatLng; import com.hitherejoe.watchtower.ui.activity.DetailActivity; import com.hitherejoe.watchtower.util.DataUtils; import com.hitherejoe.watchtower.util.DialogFactory; import com.squareup.otto.Subscribe; import java.util.ArrayList; import java.util.Arrays; import butterknife.Bind; import butterknife.ButterKnife; import retrofit.RetrofitError; import rx.Observable; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.subscriptions.CompositeSubscription; import timber.log.Timber; public class PropertiesFragment extends Fragment { @Bind(R.id.edit_text_beacon_name) EditText mBeaconNameEditText; @Bind(R.id.edit_text_advertised_id) EditText mAdvertisedIdEditText; @Bind(R.id.edit_text_description) EditText mDescriptionEditText; @Bind(R.id.edit_text_latitude) EditText mLatitudeEditText; @Bind(R.id.edit_text_longitude) EditText mLongitudeEditText; @Bind(R.id.edit_text_place_id) EditText mPlaceIdEditText; @Bind(R.id.spinner_status) Spinner mBeaconStatusSpinner; @Bind(R.id.spinner_type) Spinner mBeaconTypeSpinner; @Bind(R.id.spinner_stability) Spinner mBeaconStabilitySpinner; @Bind(R.id.text_title_beacon_name) TextView mBeaconNameText; @Bind(R.id.text_title_place_id) TextView mBeaconPlaceId; @Bind(R.id.text_title_description) TextView mBeaconDescription; @Bind(R.id.text_title_location) TextView mBeaconLocation; @Bind(R.id.text_title_type) TextView mBeaconType; @Bind(R.id.text_title_stability) TextView mBeaconStability; @Bind(R.id.text_advertised_id_error_message) TextView mAdvertisedIdErrorMessage; @Bind(R.id.text_status_error_message) TextView mStatusErrorMessage; @Bind(R.id.text_latitude_error_message) TextView mLatitudeErrorMessage; @Bind(R.id.text_longitude_error_message) TextView mLongitudeErrorMessage; private static final String EXTRA_MODE = "EXTRA_MODE"; private static final String EXTRA_BEACON = "EXTRA_BEACON"; private Beacon mBeacon; private CompositeSubscription mSubscriptions; private DataManager mDataManager; private Mode mPropertiesMode; private ProgressDialog mProgressDialog; public enum Mode { VIEW, REGISTER, UPDATE } public static PropertiesFragment newInstance(Beacon beacon, Mode mode) { PropertiesFragment propertiesFragment = new PropertiesFragment(); Bundle args = new Bundle(); args.putParcelable(EXTRA_BEACON, beacon); args.putSerializable(EXTRA_MODE, mode); propertiesFragment.setArguments(args); return propertiesFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPropertiesMode = (Mode) getArguments().getSerializable(EXTRA_MODE); if (mPropertiesMode == null) { throw new IllegalArgumentException("Properties fragment requires a mode!"); } mBeacon = getArguments().getParcelable(EXTRA_BEACON); if (mPropertiesMode == Mode.UPDATE && mBeacon == null) { throw new IllegalArgumentException("Properties fragment requires a beacon!"); } mSubscriptions = new CompositeSubscription(); mDataManager = WatchTowerApplication.get(getActivity()).getComponent().dataManager(); if (mPropertiesMode == Mode.VIEW) { WatchTowerApplication.get(getActivity()).getComponent().eventBus().register(this); } setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View fragmentView = inflater.inflate(R.layout.fragment_properties, container, false); ButterKnife.bind(this, fragmentView); disableFormFields(); if (mPropertiesMode != Mode.REGISTER) populateBeaconForm(); return fragmentView; } @Override public void onDestroyView() { super.onDestroyView(); if (mPropertiesMode == Mode.VIEW) { WatchTowerApplication.get(getActivity()).getComponent().eventBus().unregister(this); } mSubscriptions.unsubscribe(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (!(getActivity() instanceof DetailActivity)) inflater.inflate(R.menu.register, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_done: validateBeaconData(); return true; default: return super.onOptionsItemSelected(item); } } @Subscribe public void onBeaconUpdated(BusEvent.BeaconUpdated event) { mBeacon = event.beacon; populateBeaconForm(); } private void disableFormFields() { if (mPropertiesMode == Mode.VIEW) { displayTextFieldAsReadOnly(mBeaconNameEditText); displayTextFieldAsReadOnly(mAdvertisedIdEditText); displayTextFieldAsReadOnly(mDescriptionEditText); displayTextFieldAsReadOnly(mLatitudeEditText); displayTextFieldAsReadOnly(mLongitudeEditText); displayTextFieldAsReadOnly(mPlaceIdEditText); mBeaconStatusSpinner.setEnabled(false); mBeaconTypeSpinner.setEnabled(false); mBeaconStabilitySpinner.setEnabled(false); mAdvertisedIdErrorMessage.setVisibility(View.GONE); mStatusErrorMessage.setVisibility(View.GONE); mLatitudeErrorMessage.setVisibility(View.GONE); mLongitudeErrorMessage.setVisibility(View.GONE); } if (mPropertiesMode == Mode.REGISTER) { mBeaconNameText.setVisibility(View.GONE); mBeaconNameEditText.setVisibility(View.GONE); } else if (mPropertiesMode == Mode.UPDATE) { displayTextFieldAsReadOnly(mBeaconNameEditText); displayTextFieldAsReadOnly(mAdvertisedIdEditText); } } private void displayTextFieldAsReadOnly(TextView textField) { textField.setInputType(0); textField.setFocusable(false); textField.setBackground(null); } private void populateBeaconForm() { mBeaconNameEditText.setText(mBeacon.beaconName); String id = DataUtils.base64DecodeToString(mBeacon.advertisedId.id); if (id != null) mAdvertisedIdEditText.setText(id); // If properties are not defined then we hide the corresponding views if (mBeacon.placeId != null) { mPlaceIdEditText.setText(mBeacon.placeId); } else if (mPropertiesMode == Mode.VIEW) { mBeaconPlaceId.setVisibility(View.GONE); mPlaceIdEditText.setVisibility(View.GONE); } if (mBeacon.description != null) { mDescriptionEditText.setText(mBeacon.description); } else if (mPropertiesMode == Mode.VIEW) { mBeaconDescription.setVisibility(View.GONE); mDescriptionEditText.setVisibility(View.GONE); } if (mBeacon.latLng != null) { if (mBeacon.latLng.latitude != null) { mLatitudeEditText.setText(String.valueOf(mBeacon.latLng.latitude)); } else if (mPropertiesMode == Mode.VIEW) { mLatitudeEditText.setVisibility(View.GONE); mLatitudeErrorMessage.setVisibility(View.GONE); } if (mBeacon.latLng.longitude != null) { mLongitudeEditText.setText(String.valueOf(mBeacon.latLng.longitude)); } else if (mPropertiesMode == Mode.VIEW){ mLongitudeEditText.setVisibility(View.GONE); mLongitudeErrorMessage.setVisibility(View.GONE); } if (mLatitudeEditText.getVisibility() == View.GONE && mLongitudeEditText.getVisibility() == View.GONE) { mBeaconLocation.setVisibility(View.GONE); } } else if (mPropertiesMode == Mode.VIEW) { mBeaconLocation.setVisibility(View.GONE); mLatitudeEditText.setVisibility(View.GONE); mLatitudeErrorMessage.setVisibility(View.GONE); mLongitudeEditText.setVisibility(View.GONE); mLongitudeErrorMessage.setVisibility(View.GONE); } ArrayList<String> statuses = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.statuses))); mBeaconStatusSpinner.setSelection(statuses.indexOf(mBeacon.status.getString())); if (mBeacon.advertisedId.type != null) { ArrayList<String> types = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.types))); mBeaconTypeSpinner.setSelection(types.indexOf(mBeacon.advertisedId.type.getString())); } else if (mPropertiesMode == Mode.VIEW) { mBeaconType.setVisibility(View.GONE); mBeaconTypeSpinner.setVisibility(View.GONE); } if (mBeacon.expectedStability != null) { ArrayList<String> types = new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.stabilities))); mBeaconStabilitySpinner.setSelection(types.indexOf(mBeacon.expectedStability.getString())); } else if (mPropertiesMode == Mode.VIEW) { mBeaconStability.setVisibility(View.GONE); mBeaconStabilitySpinner.setVisibility(View.GONE); } } private void validateBeaconData() { boolean isValid = mAdvertisedIdEditText.getText().length() > 0 && mBeaconStatusSpinner.getSelectedItemPosition() > 0; mAdvertisedIdErrorMessage.setVisibility(mAdvertisedIdEditText.getText().length() > 0 ? View.INVISIBLE : View.VISIBLE); mStatusErrorMessage.setVisibility(mBeaconStatusSpinner.getSelectedItemPosition() > 0 ? View.INVISIBLE : View.VISIBLE); String latitude = mLatitudeEditText.getText().toString(); String longitude = mLongitudeEditText.getText().toString(); if (latitude.length() > 0) { if(!DataUtils.isStringDoubleValue(latitude)) { isValid = false; mLatitudeErrorMessage.setVisibility(View.VISIBLE); } else { mLatitudeErrorMessage.setVisibility(View.INVISIBLE); } } else { mLatitudeErrorMessage.setVisibility(View.INVISIBLE); } if (longitude.length() > 0){ if (!DataUtils.isStringDoubleValue(longitude)) { isValid = false; mLongitudeErrorMessage.setVisibility(View.VISIBLE); } else { mLongitudeErrorMessage.setVisibility(View.INVISIBLE); } } else { mLongitudeErrorMessage.setVisibility(View.INVISIBLE); } if (isValid) saveBeacon(buildBeaconObject()); } private Beacon buildBeaconObject() { AdvertisedId advertisedId = new AdvertisedId(DataUtils.base64Encode(mAdvertisedIdEditText.getText().toString().getBytes()), AdvertisedId.Type.fromString(mBeaconTypeSpinner.getSelectedItem().toString())); String latitude = mLatitudeEditText.getText().toString(); String longitude = mLatitudeEditText.getText().toString(); LatLng latLng = new LatLng(); if (latitude.length() > 0) latLng.latitude = Double.valueOf(latitude); if (longitude.length() > 0) latLng.longitude = Double.valueOf(latitude); return new Beacon.BeaconBuilder(advertisedId) .status(mBeaconStatusSpinner.getSelectedItemPosition() > 0 ? Status.fromString(mBeaconStatusSpinner.getSelectedItem().toString()) : null) .stability(mBeaconStabilitySpinner.getSelectedItemPosition() > 0 ? Stability.fromString(mBeaconStabilitySpinner.getSelectedItem().toString()) : null) .description(mDescriptionEditText.getText().toString()) .placeId(mPlaceIdEditText.getText().toString()) .latLng(latLng) .build(); } private void saveBeacon(Beacon beacon) { if (DataUtils.isNetworkAvailable(getActivity())) { showProgressDialog(); boolean hasStatusChanged = false; if (mPropertiesMode == Mode.UPDATE) hasStatusChanged = beacon.status != mBeacon.status; Observable<Beacon> beaconObservable = mPropertiesMode == Mode.UPDATE ? mDataManager.updateBeacon(mBeacon.beaconName, beacon, hasStatusChanged, beacon.status) : mDataManager.registerBeacon(beacon); mSubscriptions.add(beaconObservable .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(mDataManager.getScheduler()) .subscribe(new Subscriber<Beacon>() { @Override public void onCompleted() { mProgressDialog.dismiss(); WatchTowerApplication.get( getActivity()).getComponent().eventBus().post(new BusEvent.BeaconListAmended()); getActivity().finish(); } @Override public void onError(Throwable error) { mProgressDialog.dismiss(); Timber.d("There was an error saving the beacon : " + error.getMessage()); if (error instanceof RetrofitError) { DialogFactory.createRetrofitErrorDialog( getActivity(), (RetrofitError) error).show(); } else { DialogFactory.createSimpleErrorDialog(getActivity()).show(); } } @Override public void onNext(Beacon beacon) { if (mPropertiesMode == Mode.UPDATE) { WatchTowerApplication.get(getActivity()) .getComponent().eventBus().post(new BusEvent.BeaconUpdated(beacon)); } } })); } else { DialogFactory.createSimpleOkErrorDialog( getActivity(), getString(R.string.dialog_error_title), getString(R.string.dialog_error_no_connection) ).show(); } } private void showProgressDialog() { mProgressDialog = DialogFactory.createProgressDialog(getActivity(), R.string.progress_dialog_saving_beacon); mProgressDialog.show(); } }