/*
* AndFHEM - Open Source Android application to control a FHEM home automation
* server.
*
* Copyright (c) 2011, Matthias Klass or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLIC LICENSE, as published by the Free Software Foundation.
*
* 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package li.klass.fhem.fragments;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
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.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import li.klass.fhem.R;
import li.klass.fhem.adapter.devices.genericui.availableTargetStates.OnTargetStateSelectedCallback;
import li.klass.fhem.constants.BundleExtraKeys;
import li.klass.fhem.constants.ResultCodes;
import li.klass.fhem.dagger.ApplicationComponent;
import li.klass.fhem.domain.AtDevice;
import li.klass.fhem.domain.core.FhemDevice;
import li.klass.fhem.fragments.core.BaseFragment;
import li.klass.fhem.fragments.device.DeviceNameSelectionFragment;
import li.klass.fhem.service.intent.DeviceIntentService;
import li.klass.fhem.service.intent.RoomListIntentService;
import li.klass.fhem.util.DialogUtil;
import li.klass.fhem.util.FhemResultReceiver;
import li.klass.fhem.widget.TimePickerWithSecondsDialog;
import static com.google.common.base.Preconditions.checkNotNull;
import static li.klass.fhem.adapter.devices.genericui.AvailableTargetStatesDialogUtil.showSwitchOptionsMenu;
import static li.klass.fhem.constants.Actions.DEVICE_TIMER_MODIFY;
import static li.klass.fhem.constants.Actions.DEVICE_TIMER_NEW;
import static li.klass.fhem.constants.Actions.DISMISS_EXECUTING_DIALOG;
import static li.klass.fhem.constants.Actions.GET_DEVICE_FOR_NAME;
import static li.klass.fhem.constants.Actions.SHOW_FRAGMENT;
import static li.klass.fhem.constants.Actions.SHOW_TOAST;
import static li.klass.fhem.constants.BundleExtraKeys.CLICKED_DEVICE;
import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_ID;
import static li.klass.fhem.constants.BundleExtraKeys.DEVICE;
import static li.klass.fhem.constants.BundleExtraKeys.DEVICE_NAME;
import static li.klass.fhem.constants.BundleExtraKeys.FRAGMENT;
import static li.klass.fhem.constants.BundleExtraKeys.RESULT_RECEIVER;
import static li.klass.fhem.constants.BundleExtraKeys.STRING_ID;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_HOUR;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_IS_ACTIVE;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_MINUTE;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_REPETITION;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_SECOND;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_TARGET_DEVICE_NAME;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_TARGET_STATE;
import static li.klass.fhem.constants.BundleExtraKeys.TIMER_TYPE;
import static li.klass.fhem.fragments.FragmentType.DEVICE_SELECTION;
import static li.klass.fhem.widget.TimePickerWithSeconds.getFormattedValue;
import static org.apache.commons.lang3.StringUtils.isBlank;
public class TimerDetailFragment extends BaseFragment {
private static final DeviceNameSelectionFragment.DeviceFilter DEVICE_FILTER = new DeviceNameSelectionFragment.DeviceFilter() {
@Override
public boolean isSelectable(FhemDevice<?> device) {
return device.getSetList().size() > 0;
}
};
private static final String TAG = TimerDetailFragment.class.getName();
public AtDevice timerDevice;
private transient FhemDevice targetDevice;
private String savedTimerDeviceName;
private String connectionId;
@Override
protected void inject(ApplicationComponent applicationComponent) {
applicationComponent.inject(this);
}
@Override
public void setArguments(Bundle args) {
if (args.containsKey(DEVICE_NAME)) {
savedTimerDeviceName = args.getString(DEVICE_NAME);
}
connectionId = args.getString(CONNECTION_ID);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (view != null) {
return view;
}
view = inflater.inflate(R.layout.timer_detail, container, false);
bindRepetitionSpinner(view);
bindSelectDeviceButton(view);
bindTimerTypeSpinner(view);
bindSwitchTimeButton(view);
bindIsActiveCheckbox(view);
bindTargetStateButton(view);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EditText timerNameInput = getTimerNameInput(view);
setTimerName("", view);
if (isModify()) {
timerNameInput.setEnabled(false);
}
setTargetDeviceName("", view);
if (isModify()) {
setTimerDeviceValuesForName(savedTimerDeviceName);
}
updateTargetDevice(targetDevice, view);
updateTimerInformation(timerDevice);
updateTargetStateRowVisibility(view);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.timer_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.reset:
setValuesForCurrentTimerDevice(timerDevice);
return true;
case R.id.save:
save();
return true;
}
return super.onOptionsItemSelected(item);
}
private void bindTargetStateButton(final View view) {
getTargetStateChangeButton(view).setOnClickListener(new View.OnClickListener() {
@SuppressWarnings("unchecked")
@Override
public void onClick(View v) {
showSwitchOptionsMenu(getActivity(), targetDevice, new OnTargetStateSelectedCallback() {
@Override
public void onStateSelected(FhemDevice device, String targetState) {
setTargetState(targetState, view);
}
@Override
public void onSubStateSelected(FhemDevice device, String state, String subState) {
onStateSelected(device, state + " " + subState);
}
@Override
public void onNothingSelected(FhemDevice device) {
}
});
}
});
}
private void bindSelectDeviceButton(final View view) {
getTargetDeviceChangeButton(view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View button) {
getActivity().sendBroadcast(new Intent(SHOW_FRAGMENT)
.putExtra(FRAGMENT, DEVICE_SELECTION)
.putExtra(BundleExtraKeys.DEVICE_FILTER, DEVICE_FILTER)
.putExtra(RESULT_RECEIVER, new FhemResultReceiver() {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode != ResultCodes.SUCCESS) return;
if (!resultData.containsKey(CLICKED_DEVICE)) return;
updateTargetDevice((FhemDevice) resultData.get(CLICKED_DEVICE), view);
}
}));
}
});
}
private void bindIsActiveCheckbox(View view) {
CheckBox isActiveCheckbox = getIsActiveCheckbox(view);
if (!isModify()) {
isActiveCheckbox.setChecked(true);
isActiveCheckbox.setEnabled(false);
}
}
private void bindSwitchTimeButton(final View view) {
Button switchTimeChangeButton = (Button) view.findViewById(R.id.switchTimeSet);
switchTimeChangeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View button) {
SwitchTime switchTime = getSwitchTime(view).or(new SwitchTime(0, 0, 0));
new TimePickerWithSecondsDialog(getActivity(), switchTime.hour, switchTime.minute, switchTime.second, new TimePickerWithSecondsDialog.TimePickerWithSecondsListener() {
@Override
public void onTimeChanged(boolean okClicked, int newHour, int newMinute, int newSecond, String formattedText) {
setSwitchTime(newHour, newMinute, newSecond, view);
}
}).show();
}
});
}
private void bindTimerTypeSpinner(View view) {
Spinner typeSpinner = getTypeSpinner(view);
ArrayAdapter<String> timerTypeAdapter = new ArrayAdapter<>(getActivity(), R.layout.spinnercontent);
for (AtDevice.TimerType type : AtDevice.TimerType.values()) {
timerTypeAdapter.add(view.getContext().getString(type.getText()));
}
typeSpinner.setAdapter(timerTypeAdapter);
}
private void bindRepetitionSpinner(View view) {
Spinner repetitionSpinner = getRepetitionSpinner(view);
ArrayAdapter<String> repetitionAdapter = new ArrayAdapter<>(getActivity(), R.layout.spinnercontent);
for (AtDevice.AtRepetition atRepetition : AtDevice.AtRepetition.values()) {
repetitionAdapter.add(view.getContext().getString(atRepetition.getText()));
}
repetitionSpinner.setAdapter(repetitionAdapter);
}
private void save() {
View view = getView();
if (view == null) return;
Optional<SwitchTime> switchTimeOptional = getSwitchTime(view);
if (targetDevice == null || isBlank(getTargetState(view)) || !switchTimeOptional.isPresent()) {
getActivity().sendBroadcast(new Intent(SHOW_TOAST)
.putExtra(STRING_ID, R.string.incompleteConfigurationError));
return;
}
final String timerDeviceName = getTimerName(view);
if (!isModify()) {
if (timerDeviceName.contains(" ")) {
DialogUtil.showAlertDialog(getActivity(), R.string.error, R.string.error_timer_name_spaces);
return;
}
}
String action = isModify() ? DEVICE_TIMER_MODIFY : DEVICE_TIMER_NEW;
SwitchTime switchTime = switchTimeOptional.get();
getActivity().startService(new Intent(action)
.setClass(getActivity(), DeviceIntentService.class)
.putExtra(RESULT_RECEIVER, new FhemResultReceiver() {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == ResultCodes.SUCCESS) {
back();
savedTimerDeviceName = timerDeviceName;
}
}
})
.putExtra(TIMER_TARGET_DEVICE_NAME, targetDevice.getName())
.putExtra(TIMER_TARGET_STATE, getTargetState(view))
.putExtra(TIMER_HOUR, switchTime.hour)
.putExtra(TIMER_MINUTE, switchTime.minute)
.putExtra(TIMER_SECOND, switchTime.second)
.putExtra(TIMER_REPETITION, getRepetition(view).name())
.putExtra(TIMER_TYPE, getType(view).name())
.putExtra(TIMER_IS_ACTIVE, getIsActive(view))
.putExtra(DEVICE_NAME, timerDeviceName));
}
private void updateTargetDevice(FhemDevice targetDevice, View view) {
if (view == null || targetDevice == null) {
return;
}
setTargetDeviceName(targetDevice.getName(), view);
TimerDetailFragment.this.targetDevice = targetDevice;
if (!updateTargetStateRowVisibility(view)) {
setTargetState(getString(R.string.unknown), view);
}
}
private boolean updateTargetStateRowVisibility(View view) {
if (view == null) return false;
View targetDeviceRow = view.findViewById(R.id.targetStateRow);
if (targetDevice == null) {
targetDeviceRow.setVisibility(View.GONE);
return false;
} else {
targetDeviceRow.setVisibility(View.VISIBLE);
return true;
}
}
private void setTimerDeviceValuesForName(String timerDeviceName) {
checkNotNull(timerDeviceName);
getActivity().startService(new Intent(GET_DEVICE_FOR_NAME)
.setClass(getActivity(), RoomListIntentService.class)
.putExtra(DEVICE_NAME, timerDeviceName)
.putExtra(RESULT_RECEIVER, new FhemResultReceiver() {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode != ResultCodes.SUCCESS || !resultData.containsKey(DEVICE)) {
return;
}
FhemDevice device = (FhemDevice) resultData.getSerializable(DEVICE);
if (!(device instanceof AtDevice)) {
Log.e(TAG, "expected an AtDevice, but got " + device);
return;
}
setValuesForCurrentTimerDevice((AtDevice) device);
FragmentActivity activity = getActivity();
if (activity != null)
activity.sendBroadcast(new Intent(DISMISS_EXECUTING_DIALOG));
}
}));
}
private void setValuesForCurrentTimerDevice(AtDevice atDevice) {
this.timerDevice = atDevice;
getActivity().startService(new Intent(GET_DEVICE_FOR_NAME)
.setClass(getActivity(), RoomListIntentService.class)
.putExtra(DEVICE_NAME, atDevice.getTargetDevice())
.putExtra(RESULT_RECEIVER, new FhemResultReceiver() {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == ResultCodes.SUCCESS && resultData.containsKey(DEVICE)) {
updateTargetDevice((FhemDevice) resultData.get(DEVICE), TimerDetailFragment.this.getView());
}
}
}));
updateTimerInformation(timerDevice);
}
private void updateTimerInformation(AtDevice timerDevice) {
View view = getView();
if (view != null && timerDevice != null) {
setType(timerDevice.getTimerType(), view);
setRepetition(timerDevice.getRepetition(), view);
setIsActive(timerDevice.isActive(), view);
setSwitchTime(timerDevice.getHours(), timerDevice.getMinutes(), timerDevice.getSeconds(), view);
setTimerName(timerDevice.getName(), view);
setTargetState(Joiner.on(" ").skipNulls().join(timerDevice.getTargetState(), timerDevice.getTargetStateAddtionalInformation()), view);
}
}
private void setTimerName(String timerDeviceName, View view) {
getTimerNameInput(view).setText(timerDeviceName);
}
private String getTimerName(View view) {
return getTimerNameInput(view).getText().toString();
}
private void setTargetState(String targetState, View view) {
getTargetStateTextView(view).setText(targetState);
}
private String getTargetState(View view) {
return getTargetStateTextView(view).getText().toString();
}
private void setSwitchTime(int hour, int minute, int second, View view) {
getSwitchTimeTextView(view).setText(getFormattedValue(hour, minute, second));
}
private Optional<SwitchTime> getSwitchTime(View view) {
String text = getSwitchTimeTextView(view).getText().toString();
ImmutableList<String> parts = ImmutableList.copyOf(Splitter.on(":").split(text));
if (parts.size() != 3) {
return Optional.absent();
}
return Optional.of(new SwitchTime(
Integer.parseInt(parts.get(0)),
Integer.parseInt(parts.get(1)),
Integer.parseInt(parts.get(2))
));
}
private void setIsActive(boolean isActive, View view) {
getIsActiveCheckbox(view).setChecked(isActive);
}
private boolean getIsActive(View view) {
return getIsActiveCheckbox(view).isChecked();
}
private void setRepetition(AtDevice.AtRepetition repetition, View view) {
getRepetitionSpinner(view).setSelection(repetition.ordinal());
}
private AtDevice.AtRepetition getRepetition(View view) {
return AtDevice.AtRepetition.values()[getRepetitionSpinner(view).getSelectedItemPosition()];
}
private void setType(AtDevice.TimerType type, View view) {
getTypeSpinner(view).setSelection(type.ordinal());
}
private AtDevice.TimerType getType(View view) {
return AtDevice.TimerType.values()[getTypeSpinner(view).getSelectedItemPosition()];
}
@Override
public void update(boolean doUpdate) {
}
@Override
public CharSequence getTitle(Context context) {
return context.getString(R.string.timer);
}
private EditText getTimerNameInput(View view) {
return (EditText) view.findViewById(R.id.timerNameInput);
}
private Spinner getRepetitionSpinner(View view) {
return (Spinner) view.findViewById(R.id.timerRepetition);
}
private CheckBox getIsActiveCheckbox(View view) {
return (CheckBox) view.findViewById(R.id.isActive);
}
private Spinner getTypeSpinner(View view) {
return (Spinner) view.findViewById(R.id.timerType);
}
private TextView getSwitchTimeTextView(View view) {
return (TextView) view.findViewById(R.id.switchTimeContent);
}
private TextView getTargetDeviceTextView(View view) {
return (TextView) view.findViewById(R.id.targetDeviceName);
}
private Button getTargetDeviceChangeButton(View view) {
return (Button) view.findViewById(R.id.targetDeviceSet);
}
private EditText getTargetStateTextView(View view) {
return (EditText) view.findViewById(R.id.targetState);
}
private Button getTargetStateChangeButton(View view) {
return (Button) view.findViewById(R.id.targetStateSet);
}
private void setTargetDeviceName(String string, View view) {
getTargetDeviceTextView(view).setText(string);
}
private boolean isModify() {
return !Strings.isNullOrEmpty(savedTimerDeviceName);
}
private static class SwitchTime {
final int hour;
final int minute;
final int second;
SwitchTime(int hour, int minute, int second) {
this.hour = hour;
this.minute = minute;
this.second = second;
}
}
}