package com.radicaldynamic.groupinform.activities; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.widget.EditText; import android.widget.RadioButton; import android.widget.Toast; import com.radicaldynamic.groupinform.R; import com.radicaldynamic.groupinform.application.Collect; import com.radicaldynamic.groupinform.logic.AccountFolder; import com.radicaldynamic.groupinform.logic.InformOnlineState; import com.radicaldynamic.groupinform.utilities.HttpUtils; import com.radicaldynamic.groupinform.utilities.FileUtilsExtended; public class AccountFolderActivity extends Activity { private static final String t = "AccountFolderActivity: "; public static final String KEY_NEW_FOLDER = "new_folder"; public static final String KEY_FOLDER_ID = "folder_id"; public static final String KEY_FOLDER_REV = "folder_rev"; public static final String KEY_FOLDER_OWNER = "folder_owner"; public static final String KEY_FOLDER_NAME = "folder_name"; public static final String KEY_FOLDER_DESC = "folder_description"; public static final String KEY_FOLDER_VISIBILITY = "folder_visibility"; private static final int MENU_REMOVE_FOLDER = 0; public static final int SAVING_DIALOG = 0; public static final int REMOVING_DIALOG = 1; public static final int CONFIRM_REMOVAL_DIALOG = 2; // Visibility status strings used by Inform Online public static final String PRIVATE_FOLDER = "private"; public static final String SHARED_FOLDER = "shared"; private AlertDialog mAlertDialog; private ProgressDialog mProgressDialog; private AccountFolder mFolder; private EditText mFolderName; private EditText mFolderDescription; private RadioButton mFolderVisibilityShared; private RadioButton mFolderVisibilityPrivate; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.folder); setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_account_folder)); if (savedInstanceState == null) { Intent i = getIntent(); if (i == null) { // New folder } else { if (i.getBooleanExtra(KEY_NEW_FOLDER, false)) { mFolder = new AccountFolder(null, null, null, "", "", "private", false); } else { // Because we don't have a better way of shuffling these values around mFolder = new AccountFolder( i.getStringExtra(KEY_FOLDER_ID), i.getStringExtra(KEY_FOLDER_REV), i.getStringExtra(KEY_FOLDER_OWNER), i.getStringExtra(KEY_FOLDER_NAME), i.getStringExtra(KEY_FOLDER_DESC), i.getStringExtra(KEY_FOLDER_VISIBILITY), false); } } } else { Object data = getLastNonConfigurationInstance(); if (data instanceof AccountFolder) { mFolder = (AccountFolder) data; } } mFolderName = (EditText) findViewById(R.id.folderName); mFolderDescription = (EditText) findViewById(R.id.folderDescription); mFolderVisibilityPrivate = (RadioButton) findViewById(R.id.folderVisibilityPrivate); mFolderVisibilityShared = (RadioButton) findViewById(R.id.folderVisibilityShared); mFolderName.setText(mFolder.getName()); mFolderDescription.setText(mFolder.getDescription()); // Initialize fields based on whether the folder is private if (mFolder.getVisibility().equals(PRIVATE_FOLDER)) { mFolderVisibilityPrivate.setChecked(true); } else { mFolderVisibilityShared.setChecked(true); } } /* * (non-Javadoc) * * @see android.app.Activity#onCreateDialog(int) */ @Override protected Dialog onCreateDialog(int id) { switch (id) { case SAVING_DIALOG: mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getText(R.string.tf_saving_please_wait)); mProgressDialog.setIndeterminate(true); mProgressDialog.setCancelable(false); return mProgressDialog; case REMOVING_DIALOG: mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getText(R.string.tf_removing_please_wait)); mProgressDialog.setIndeterminate(true); mProgressDialog.setCancelable(false); return mProgressDialog; case CONFIRM_REMOVAL_DIALOG: mAlertDialog = new AlertDialog.Builder(this) .setIcon(R.drawable.ic_dialog_alert) .setTitle(getString(R.string.tf_remove_folder) + "?") .setMessage(R.string.tf_remove_folder_dialog_msg) .setPositiveButton(R.string.tf_remove, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { new RemoveFolderTask().execute(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .create(); mAlertDialog.show(); break; } return null; } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); Boolean enabled = true; // Certain menu entries should not be available when creating a new folder if (mFolder.getId() == null) enabled = false; menu.add(0, MENU_REMOVE_FOLDER, 0, getString(R.string.tf_remove_folder)).setIcon(R.drawable.ic_menu_delete).setEnabled(enabled); return true; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: showQuitDialog(); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_REMOVE_FOLDER: showDialog(CONFIRM_REMOVAL_DIALOG); return true; } return super.onOptionsItemSelected(item); } @Override public Object onRetainNonConfigurationInstance() { return mFolder; } private class CommitChangesTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... nothing) { List<NameValuePair> params = new ArrayList<NameValuePair>(); // New folder getId() values are null if (mFolder.getId() != null) { params.add(new BasicNameValuePair("folderId", mFolder.getId())); params.add(new BasicNameValuePair("folderRev", mFolder.getRev())); } params.add(new BasicNameValuePair("name", mFolderName.getText().toString().trim())); params.add(new BasicNameValuePair("description", mFolderDescription.getText().toString().trim())); if (mFolderVisibilityPrivate.isChecked()) params.add(new BasicNameValuePair("visibility", PRIVATE_FOLDER)); else params.add(new BasicNameValuePair("visibility", SHARED_FOLDER)); String processUrl; if (mFolder.getId() == null) processUrl = Collect.getInstance().getInformOnlineState().getServerUrl() + "/folder/add"; else processUrl = Collect.getInstance().getInformOnlineState().getServerUrl() + "/folder/update"; return HttpUtils.postUrlData(processUrl, params); } @Override protected void onPreExecute() { showDialog(SAVING_DIALOG); } @Override protected void onPostExecute(String postResult) { JSONObject update; mProgressDialog.cancel(); try { update = (JSONObject) new JSONTokener(postResult).nextValue(); String result = update.optString(InformOnlineState.RESULT, InformOnlineState.FAILURE); // Update successful if (result.equals(InformOnlineState.OK)) { Toast.makeText(getApplicationContext(), getString(R.string.tf_saved_data), Toast.LENGTH_SHORT).show(); // Force the list to refresh (do not be destructive in case something bad happens later) new File(getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE).setLastModified(0); // Get out of here finish(); } else { // Something bad happened if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "system error while processing postResult"); Toast.makeText(getApplicationContext(), getString(R.string.tf_system_error_dialog_msg), Toast.LENGTH_LONG).show(); } } catch (NullPointerException e) { // Communication error if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "no postResult to parse. Communication error with node.js server?"); Toast.makeText(getApplicationContext(), getString(R.string.tf_communication_error_try_again), Toast.LENGTH_LONG).show(); e.printStackTrace(); } catch (JSONException e) { // Parse error (malformed result) if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "failed to parse postResult " + postResult); Toast.makeText(getApplicationContext(), getString(R.string.tf_system_error_dialog_msg), Toast.LENGTH_LONG).show(); e.printStackTrace(); } } } private class RemoveFolderTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... nothing) { String removeUrl = Collect.getInstance().getInformOnlineState().getServerUrl() + "/folder/remove/" + mFolder.getId() + File.separator + mFolder.getRev(); return HttpUtils.getUrlData(removeUrl); } @Override protected void onPreExecute() { showDialog(REMOVING_DIALOG); } @Override protected void onPostExecute(String getResult) { mProgressDialog.cancel(); JSONObject update; try { if (Collect.Log.DEBUG) Log.d(Collect.LOGTAG, t + "parsing getResult " + getResult); update = (JSONObject) new JSONTokener(getResult).nextValue(); String result = update.optString(InformOnlineState.RESULT, InformOnlineState.ERROR); // Update successful if (result.equals(InformOnlineState.OK)) { Toast.makeText(getApplicationContext(), getString(R.string.tf_removed_with_param, mFolder.getName()), Toast.LENGTH_SHORT).show(); // Force the list to refresh (do not be destructive in case something bad happens later) new File(getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE).setLastModified(0); // Get out of here finish(); } else if (result.equals(InformOnlineState.FAILURE)) { // There is only one possible failure right now (the user tried to remove their default DB) Toast.makeText(getApplicationContext(), getString(R.string.tf_unable_to_remove_defaultdb), Toast.LENGTH_LONG).show(); } else { // Something bad happened if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "system error while processing getResult"); Toast.makeText(getApplicationContext(), getString(R.string.tf_system_error_dialog_msg), Toast.LENGTH_LONG).show(); } } catch (NullPointerException e) { // Communication error if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "no getResult to parse. Communication error with node.js server?"); Toast.makeText(getApplicationContext(), getString(R.string.tf_communication_error_try_again), Toast.LENGTH_LONG).show(); e.printStackTrace(); } catch (JSONException e) { // Parse error (malformed result) if (Collect.Log.ERROR) Log.e(Collect.LOGTAG, t + "failed to parse getResult " + getResult); Toast.makeText(getApplicationContext(), getString(R.string.tf_system_error_dialog_msg), Toast.LENGTH_LONG).show(); e.printStackTrace(); } } } /* * Prompt shown to the user before they leave the field list * (discard changes & quit, save changes & quit, return to form field list) */ private void showQuitDialog() { String[] items = { getString(R.string.do_not_save), getString(R.string.keep_changes), getString(R.string.tf_abort_exit) }; mAlertDialog = new AlertDialog.Builder(this) .setIcon(R.drawable.ic_dialog_alert) .setTitle(getString(R.string.quit_application, "Without Saving?")) .setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: // Discard any changes and exit finish(); break; case 1: // Save and exit if (mFolderName.getText().toString().trim().length() > 0) new CommitChangesTask().execute(); else Toast.makeText(getApplicationContext(), getString(R.string.tf_folder_name_required), Toast.LENGTH_LONG).show(); break; case 2: // Do nothing break; } } }).create(); mAlertDialog.show(); } }