package org.droidplanner.android.fragments;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.util.TypedValue;
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.AbsListView;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.o3dr.android.client.Drone;
import com.o3dr.android.client.apis.VehicleApi;
import com.o3dr.services.android.lib.drone.attribute.AttributeEvent;
import com.o3dr.services.android.lib.drone.attribute.AttributeEventExtra;
import com.o3dr.services.android.lib.drone.attribute.AttributeType;
import com.o3dr.services.android.lib.drone.property.Parameter;
import com.o3dr.services.android.lib.drone.property.Parameters;
import org.droidplanner.android.R;
import org.droidplanner.android.dialogs.SupportEditInputDialog;
import org.droidplanner.android.dialogs.openfile.OpenParameterDialog;
import org.droidplanner.android.dialogs.parameters.DialogParameterInfo;
import org.droidplanner.android.fragments.helpers.ApiListenerListFragment;
import org.droidplanner.android.utils.file.DirectoryPath;
import org.droidplanner.android.utils.file.FileList;
import org.droidplanner.android.utils.file.FileStream;
import org.droidplanner.android.utils.file.IO.ParameterWriter;
import org.droidplanner.android.utils.prefs.DroidPlannerPrefs;
import org.droidplanner.android.view.adapterViews.ParamsAdapter;
import org.droidplanner.android.view.adapterViews.ParamsAdapterItem;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
public class
ParamsFragment extends ApiListenerListFragment implements
SupportEditInputDialog.Listener {
public static final String ADAPTER_ITEMS = ParamsFragment.class.getName() + ".adapter.items";
private static final String PREF_PARAMS_FILTER_ON = "pref_params_filter_on";
private static final boolean DEFAULT_PARAMS_FILTER_ON = true;
private static final String EXTRA_OPENED_PARAMS_FILENAME = "extra_opened_params_filename";
private final static IntentFilter intentFilter = new IntentFilter();
public static final int SNACKBAR_HEIGHT = 48;
private static final String PARAMETERS_FILENAME_DIALOG_TAG = "Parameters filename";
static {
intentFilter.addAction(AttributeEvent.PARAMETERS_REFRESH_STARTED);
intentFilter.addAction(AttributeEvent.PARAMETERS_REFRESH_COMPLETED);
intentFilter.addAction(AttributeEvent.PARAMETER_RECEIVED);
intentFilter.addAction(AttributeEvent.STATE_CONNECTED);
intentFilter.addAction(AttributeEvent.TYPE_UPDATED);
}
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
switch (action) {
case AttributeEvent.PARAMETERS_REFRESH_STARTED:
startProgress();
break;
case AttributeEvent.PARAMETERS_REFRESH_COMPLETED:
stopProgress();
/*** FALL - THROUGH ***/
case AttributeEvent.STATE_CONNECTED:
case AttributeEvent.TYPE_UPDATED:
final Drone drone = getDrone();
if (drone != null && drone.isConnected()) {
final Parameters droneParams = drone.getAttribute(AttributeType.PARAMETERS);
loadAdapter(droneParams.getParameters(), false);
}
break;
case AttributeEvent.PARAMETER_RECEIVED:
final int defaultValue = -1;
int index = intent.getIntExtra(AttributeEventExtra.EXTRA_PARAMETER_INDEX, defaultValue);
int count = intent.getIntExtra(AttributeEventExtra.EXTRA_PARAMETERS_COUNT, defaultValue);
if (index != defaultValue && count != defaultValue)
updateProgress(index, count);
break;
case AttributeEvent.STATE_DISCONNECTED:
stopProgress();
break;
}
}
};
private ProgressDialog progressDialog;
private SearchView searchParams;
private ProgressBar mLoadingProgress;
private DroidPlannerPrefs mPrefs;
private ParamsAdapter adapter;
/**
* If the parameters were loaded from a file, the filename is stored here.
*/
private String openedParamsFilename;
private View searchButton;
private Snackbar snackbar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mPrefs = DroidPlannerPrefs.getInstance(getActivity().getApplicationContext());
// create adapter
if (savedInstanceState != null) {
this.openedParamsFilename = savedInstanceState.getString(EXTRA_OPENED_PARAMS_FILENAME);
// load adapter items
@SuppressWarnings("unchecked")
final ArrayList<ParamsAdapterItem> pwms = savedInstanceState.getParcelableArrayList(ADAPTER_ITEMS);
adapter = new ParamsAdapter(getActivity(), R.layout.row_params, pwms);
} else {
// empty adapter
adapter = new ParamsAdapter(getActivity(), R.layout.row_params);
}
setListAdapter(adapter);
// help handler
adapter.setOnInfoListener(new ParamsAdapter.OnInfoListener() {
@Override
public void onHelp(int position, EditText valueView) {
showInfo(position, valueView);
}
});
adapter.setOnParametersChangeListener(new ParamsAdapter.OnParametersChangeListener() {
@Override
public void onParametersChange(int dirtyCount) {
if (dirtyCount > 0) {
View view = getView();
if (view != null && snackbar == null) {
snackbar = Snackbar.make(view, R.string.unsaved_param_warning, Snackbar.LENGTH_INDEFINITE)
.setAction(getString(R.string.upload), new View.OnClickListener() {
@Override
public void onClick(View v) {
writeModifiedParametersToDrone();
}
});
snackbar.show();
}
} else {
if (snackbar != null) {
snackbar.dismiss();
snackbar = null;
}
}
}
});
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// bind & initialize UI
return inflater.inflate(R.layout.fragment_params, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
searchParams = (SearchView) view.findViewById(R.id.params_filter);
searchParams.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
filterInput(s);
return false;
}
@Override
public boolean onQueryTextChange(String s) {
filterInput(s);
return true;
}
});
searchButton = searchParams.findViewById(android.support.v7.appcompat.R.id.search_button);
searchParams.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
searchButton.performClick();
else
searchButton.callOnClick();
}
});
mLoadingProgress = (ProgressBar) view.findViewById(R.id.reload_progress);
mLoadingProgress.setVisibility(View.GONE);
View space = new View(getActivity().getApplicationContext());
space.setLayoutParams(new AbsListView.LayoutParams(0, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SNACKBAR_HEIGHT, getResources().getDisplayMetrics())));
getListView().addFooterView(space);
}
private void filterInput(CharSequence input) {
if (TextUtils.isEmpty(input)) {
adapter.getFilter().filter("");
} else {
adapter.getFilter().filter(input);
}
}
@Override
public void onApiConnected() {
final Parameters droneParams = getDrone().getAttribute(AttributeType.PARAMETERS);
if (adapter.isEmpty() && droneParams != null) {
List<Parameter> parametersList = droneParams.getParameters();
if (!parametersList.isEmpty())
loadAdapter(parametersList, false);
}
toggleParameterFilter(isParameterFilterVisible());
getBroadcastManager().registerReceiver(broadcastReceiver, intentFilter);
}
@Override
public void onApiDisconnected() {
getBroadcastManager().unregisterReceiver(broadcastReceiver);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// save adapter items
final ArrayList<ParamsAdapterItem> pwms = new ArrayList<ParamsAdapterItem>(adapter.getOriginalValues());
outState.putParcelableArrayList(ADAPTER_ITEMS, pwms);
outState.putString(EXTRA_OPENED_PARAMS_FILENAME, this.openedParamsFilename);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_parameters, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
adapter.clearFocus();
switch (item.getItemId()) {
case R.id.menu_download_parameters:
refreshParameters();
break;
case R.id.menu_write_parameters:
writeModifiedParametersToDrone();
break;
case R.id.menu_open_parameters:
openParametersFromFile();
break;
case R.id.menu_save_parameters:
saveParametersToFile();
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
private void toggleParameterFilter(boolean isVisible) {
if (isVisible) {
//Show the parameter filter
searchParams.setVisibility(View.VISIBLE);
filterInput(searchParams.getQuery());
} else {
//Hide the parameter filter
searchParams.setVisibility(View.GONE);
filterInput(null);
}
mPrefs.prefs.edit().putBoolean(PREF_PARAMS_FILTER_ON, isVisible).apply();
}
private boolean isParameterFilterVisible() {
return mPrefs.prefs.getBoolean(PREF_PARAMS_FILTER_ON, DEFAULT_PARAMS_FILTER_ON);
}
private void showInfo(int position, EditText valueView) {
final ParamsAdapterItem item = adapter.getItem(position);
if (!item.getParameter().hasInfo())
return;
DialogParameterInfo.build(item, valueView, getActivity()).show();
}
private void refreshParameters() {
if (getDrone().isConnected()) {
VehicleApi.getApi(getDrone()).refreshParameters();
} else {
Toast.makeText(getActivity(), R.string.msg_connect_first, Toast.LENGTH_SHORT).show();
}
}
private void writeModifiedParametersToDrone() {
final Drone drone = getDrone();
if (!drone.isConnected())
return;
final int adapterCount = adapter.getCount();
List<Parameter> parametersList = new ArrayList<Parameter>(adapterCount);
for (int i = 0; i < adapterCount; i++) {
final ParamsAdapterItem item = adapter.getItem(i);
if (!item.isDirty())
continue;
parametersList.add(item.getParameter());
item.commit();
}
final int parametersCount = parametersList.size();
if (parametersCount > 0) {
VehicleApi.getApi(drone).writeParameters(new Parameters(parametersList));
adapter.notifyDataSetChanged();
Toast.makeText(getActivity(),
parametersCount + " " + getString(R.string.msg_parameters_written_to_drone),
Toast.LENGTH_SHORT).show();
}
snackbar = null;
}
private void openParametersFromFile() {
OpenParameterDialog dialog = new OpenParameterDialog() {
@Override
public void parameterFileLoaded(String filepath, List<Parameter> parameters) {
openedParamsFilename = filepath;
loadAdapter(parameters, true);
}
};
dialog.openDialog(getActivity(), DirectoryPath.getParametersPath(),
FileList.getParametersFileList());
}
private void saveParametersToFile() {
final String defaultFilename = TextUtils.isEmpty(openedParamsFilename)
? FileStream.getParameterFilename("Parameters")
: openedParamsFilename;
final SupportEditInputDialog dialog = SupportEditInputDialog.newInstance(PARAMETERS_FILENAME_DIALOG_TAG,
getString(R.string.label_enter_filename), defaultFilename, true);
dialog.show(getChildFragmentManager(), PARAMETERS_FILENAME_DIALOG_TAG);
}
@Override
public void onOk(String dialogTag, CharSequence input) {
switch(dialogTag){
case PARAMETERS_FILENAME_DIALOG_TAG:
final List<Parameter> parameters = new ArrayList<Parameter>();
for (int i = 0; i < adapter.getCount(); i++) {
parameters.add(adapter.getItem(i).getParameter());
}
if (parameters.size() > 0) {
ParameterWriter parameterWriter = new ParameterWriter(parameters);
if (parameterWriter.saveParametersToFile(input.toString())) {
Toast.makeText(getActivity(), R.string.parameters_saved, Toast.LENGTH_SHORT).show();
}
}
break;
}
}
@Override
public void onCancel(String dialogTag) {
}
private void loadAdapter(List<Parameter> parameters, boolean isUpdate) {
if (parameters == null || parameters.isEmpty()) {
return;
}
TreeMap<String, Parameter> prunedParameters = new TreeMap<String, Parameter>();
for (Parameter parameter : parameters) {
prunedParameters.put(parameter.getName(), parameter);
}
if (isUpdate) {
adapter.updateParameters(prunedParameters);
} else {
adapter.loadParameters(prunedParameters);
}
filterInput(searchParams.getQuery());
}
private void startProgress() {
final Activity activity = getActivity();
if(activity == null)
return;
progressDialog = new ProgressDialog(activity);
progressDialog.setTitle(R.string.refreshing_parameters);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setIndeterminate(true);
progressDialog.setCancelable(false);
progressDialog.setCanceledOnTouchOutside(true);
progressDialog.show();
mLoadingProgress.setIndeterminate(true);
mLoadingProgress.setVisibility(View.VISIBLE);
}
private void updateProgress(int progress, int max) {
if (progressDialog == null) {
startProgress();
}
if (progressDialog.isIndeterminate()) {
progressDialog.setIndeterminate(false);
progressDialog.setMax(max);
}
progressDialog.setProgress(progress);
if (mLoadingProgress.isIndeterminate()) {
mLoadingProgress.setIndeterminate(false);
mLoadingProgress.setMax(max);
}
mLoadingProgress.setProgress(progress);
}
private void stopProgress() {
// dismiss progress dialog
if (progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
mLoadingProgress.setVisibility(View.GONE);
}
}