diff --git a/res/values/strings.xml b/res/values/strings.xml index 3d92f35..446e24e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -113,10 +113,12 @@ 1 Recipe Scaled 1 Recipe Copied 1 Recipe Exported + All Recipes Exported Import Recipe(s) from XML Select a BeerXML (.xml) or BeerSmith2 (.bsmx) file to import recipes. - GDrive + GDrive + SD Card diff --git a/src/com/biermacht/brews/frontend/IngredientActivities/AddEditActivity.java b/src/com/biermacht/brews/frontend/IngredientActivities/AddEditActivity.java index 413fb35..a685305 100644 --- a/src/com/biermacht/brews/frontend/IngredientActivities/AddEditActivity.java +++ b/src/com/biermacht/brews/frontend/IngredientActivities/AddEditActivity.java @@ -1,6 +1,7 @@ package com.biermacht.brews.frontend.IngredientActivities; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v7.app.AlertDialog; @@ -29,6 +30,7 @@ import com.biermacht.brews.utils.AlertBuilder; import com.biermacht.brews.utils.Callbacks.Callback; import com.biermacht.brews.utils.Constants; +import com.biermacht.brews.utils.DriveActivity; import com.biermacht.brews.utils.IngredientHandler; import java.util.ArrayList; @@ -40,7 +42,7 @@ * examples would be Recipes, MashProfiles, or Settings. This class defines a framework for adding * views, acquiring values from those views, saving or deleting objects, and more. */ -public abstract class AddEditActivity extends AppCompatActivity implements OnClickListener { +public abstract class AddEditActivity extends DriveActivity implements OnClickListener { // Main view - holds all the rows public ViewGroup mainView; @@ -413,6 +415,16 @@ public void onClick(View v) { } } + @Override + public void onDriveFilePicked(Intent data) { + // Not used. + } + + @Override + public void onDriveFileWritten(Intent data) { + // Not used. + } + public void registerViews(List views) { if (this.registeredViews == null) { Log.d("AddEditActivity", "Initializing registeredViews"); diff --git a/src/com/biermacht/brews/frontend/MainActivity.java b/src/com/biermacht/brews/frontend/MainActivity.java index 7f8782c..9b09eae 100644 --- a/src/com/biermacht/brews/frontend/MainActivity.java +++ b/src/com/biermacht/brews/frontend/MainActivity.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentSender; import android.content.SharedPreferences; import android.content.res.Configuration; import android.net.Uri; @@ -17,7 +16,6 @@ import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; @@ -45,12 +43,10 @@ import com.biermacht.brews.tasks.ImportXmlIngredientsTask; import com.biermacht.brews.tasks.InitializeTask; import com.biermacht.brews.utils.Constants; +import com.biermacht.brews.utils.DriveActivity; import com.biermacht.brews.utils.IngredientHandler; import com.biermacht.brews.utils.comparators.ToStringComparator; import com.biermacht.brews.utils.interfaces.BiermachtFragment; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.drive.Drive; import com.google.android.gms.drive.DriveApi; @@ -67,7 +63,7 @@ import java.util.Collections; import java.util.Iterator; -public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { +public class MainActivity extends DriveActivity { // Globals, referenced outside of this Activity. // TODO: These should not be use globally - can cause null reference when application is killed and re-started. @@ -96,10 +92,6 @@ public class MainActivity extends AppCompatActivity implements GoogleApiClient.C private ActionBarDrawerToggle mDrawerToggle; private ListView drawerListView; - // Google Play API client - private GoogleApiClient mGoogleApiClient; - private ProgressDialog progressDialog; - // Currently selected drawer item - for use as an index in drawerItems and fragmentList. private int selectedItem; @@ -121,17 +113,6 @@ public void onCreate(Bundle savedInstanceState) { // Instantiate ingredient handler ingredientHandler = new IngredientHandler(getApplicationContext()); - // Create the Google API client used for accessing Google Services such as Drive. - mGoogleApiClient = new GoogleApiClient.Builder(this) - .addApi(Drive.API) - .addScope(Drive.SCOPE_FILE) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - - // Create the progress dialog view - displayed when connecting to Google APIs. - progressDialog = new ProgressDialog(MainActivity.this); - // Get shared preferences preferences = this.getSharedPreferences(Constants.PREFERENCES, Context.MODE_PRIVATE); @@ -216,14 +197,6 @@ public void onDrawerOpened(View drawerView) { selectItem(0); } - @Override - protected void onStop() { - // Disconnect from Google APIs. - mGoogleApiClient.disconnect(); - - super.onStop(); - } - @Override public void onResume() { super.onResume(); @@ -318,21 +291,9 @@ public void onClick(DialogInterface dialog, int which) { } } }) - .setNegativeButton(R.string.import_recipes_drive_button, new DialogInterface.OnClickListener() { + .setNegativeButton(R.string.drive_button, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - if (! mGoogleApiClient.isConnected()) { - Log.d("MainActivity", "Connecting to Google API"); - progressDialog.setMessage("Connecting to Google APIs..."); - progressDialog.setIndeterminate(false); - progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - progressDialog.setCancelable(false); - progressDialog.show(); - mGoogleApiClient.connect(); - } - else { - // Already connected - just show the picker. - startGoogleDrivePicker(); - } + pickFile(); } }) .setNeutralButton(R.string.cancel, null); @@ -378,7 +339,6 @@ public void onClick(DialogInterface dialog, int which) { Snackbar.make(findViewById(R.id.drawer_layout), snack, Snackbar.LENGTH_LONG).show(); updateFragments(); } - }); } @@ -418,60 +378,66 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d("MainActivity", "Load recipes from file cancelled by user"); } } - else if (requestCode == Constants.REQUEST_CONNECT_TO_DRIVE) { - Log.d("MainActivity", "Result from Google API: " + resultCode); - if (resultCode == RESULT_OK) { - // Make sure the app is not already connected or attempting to connect - if (! mGoogleApiClient.isConnecting() && - ! mGoogleApiClient.isConnected()) { - mGoogleApiClient.connect(); + else { + // If none of the above handle the response, see if the super class handles it. + // The DriveActivity superclass handles responses to Google Drive intents. + super.onActivityResult(requestCode, resultCode, data); + } + } + + /** + * Called by the DriveActivity when a drive file has been selected by a user. + * + * @param data + */ + public void onDriveFilePicked(Intent data) { + DriveId driveId = data.getParcelableExtra(OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID); + final DriveFile file = Drive.DriveApi.getFile(driveClient, driveId); + + // Callback for when the file has been opened. Checks for success and extracts the contents + // of the file. + final ResultCallback resultCallback = new ResultCallback() { + @Override + public void onResult(DriveApi.DriveContentsResult result) { + if (! result.getStatus().isSuccess()) { + // TODO: Display an error saying file can't be opened + return; } + // DriveContents object contains pointers to the actual byte stream. + DriveContents contents = result.getDriveContents(); + + // Load the recipes in the file. + new LoadRecipes(contents.getInputStream(), fileName, ingredientHandler).execute(""); } - } - else if (requestCode == Constants.REQUEST_DRIVE_FILE) { - Log.d("MainActivity", "Result from Google Drive file access: " + resultCode); - if (resultCode == RESULT_OK) { - DriveId driveId = data.getParcelableExtra(OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID); - final DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, driveId); - - // Callback for when the file has been opened. Checks for success and extracts the contents - // of the file. - final ResultCallback resultCallback = new ResultCallback() { - @Override - public void onResult(DriveApi.DriveContentsResult result) { - if (! result.getStatus().isSuccess()) { - // TODO: Display an error saying file can't be opened - return; - } - // DriveContents object contains pointers to the actual byte stream. - DriveContents contents = result.getDriveContents(); - - // Load the recipes in the file. - new LoadRecipes(contents.getInputStream(), fileName, ingredientHandler).execute(""); - } - }; - - // Callback for when file Metadata has been returned - extracts the file name - // and opens the file. - ResultCallback metadataCallback = new ResultCallback() { - @Override - public void onResult(DriveResource.MetadataResult result) { - if (! result.getStatus().isSuccess()) { - // TODO: Display an error saying file can't be opened - return; - } - // Set fileName for use in LoadRecipes. - fileName = result.getMetadata().getTitle() + result.getMetadata().getFileExtension(); - - // Open the file. - file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(resultCallback); - } - }; + }; - // Get Metadata for the file. - file.getMetadata(mGoogleApiClient).setResultCallback(metadataCallback); + // Callback for when file Metadata has been returned - extracts the file name + // and opens the file. + ResultCallback metadataCallback = new ResultCallback() { + @Override + public void onResult(DriveResource.MetadataResult result) { + if (! result.getStatus().isSuccess()) { + // TODO: Display an error saying file can't be opened + return; + } + // Set fileName for use in LoadRecipes. + fileName = result.getMetadata().getTitle() + result.getMetadata().getFileExtension(); + + // Open the file. + file.open(driveClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(resultCallback); } - } + }; + + // Get Metadata for the file. + file.getMetadata(driveClient).setResultCallback(metadataCallback); + } + + @Override + public void onDriveFileWritten(Intent data) { + Log.d("MainActivity", "File written to drive"); + + // Show a snack bar showing that it was exported. + ((RecipesFragment) fragmentList.get(0)).recipesExportedSnackbar(); } @Override @@ -487,51 +453,8 @@ public void onConfigurationChanged(Configuration newConfig) { mDrawerToggle.onConfigurationChanged(newConfig); } - @Override - public void onConnected(Bundle bundle) { - Log.d("MainActivity", "Connected to Google APIs"); - if (progressDialog.isShowing()) { - Log.d("MainActivity", "Dismissing progress dialog"); - progressDialog.dismiss(); - } - startGoogleDrivePicker(); - } - - public void startGoogleDrivePicker() { - Log.d("MainActivity", "Starting Google Drive file picker intent"); - IntentSender intentSender = Drive.DriveApi - .newOpenFileActivityBuilder() - .build(mGoogleApiClient); - try { - startIntentSenderForResult( - intentSender, Constants.REQUEST_DRIVE_FILE, null, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - e.printStackTrace(); - } - } - - @Override - public void onConnectionSuspended(int i) { - - } - - @Override - public void onConnectionFailed(ConnectionResult result) { - Log.d("MainActivity", "Google API Connection failed"); - if (result.hasResolution()) { - try { - result.startResolutionForResult(this, Constants.REQUEST_CONNECT_TO_DRIVE); - } catch (IntentSender.SendIntentException e) { - mGoogleApiClient.connect(); - } - } - else { - GoogleApiAvailability.getInstance().getErrorDialog(this, result.getErrorCode(), Constants.REQUEST_CONNECT_TO_DRIVE).show(); - } - } - /** - * Private class which handle¬¬s selections in the app drawer and selects the appropriate Fragment + * Private class which handles selections in the app drawer and selects the appropriate Fragment * to display. */ private class DrawerItemClickListener implements ListView.OnItemClickListener { diff --git a/src/com/biermacht/brews/frontend/SettingsActivity.java b/src/com/biermacht/brews/frontend/SettingsActivity.java index bcb760e..b5d3b94 100644 --- a/src/com/biermacht/brews/frontend/SettingsActivity.java +++ b/src/com/biermacht/brews/frontend/SettingsActivity.java @@ -3,8 +3,10 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; +import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.View; @@ -123,6 +125,16 @@ public void onNothingSelected(AdapterView parentView) { }); } + @Override + public void onDriveFilePicked(Intent data) { + // Not used. + } + + @Override + public void onDriveFileWritten(Intent data) { + Snackbar.make(mainView, R.string.recipes_exported, Snackbar.LENGTH_LONG).show(); + } + @Override public void onRecipeNotFound() { // We don't need a recipe for this, so do nothing. @@ -218,15 +230,21 @@ private AlertDialog.Builder exportRecipes() { return new AlertDialog.Builder(this) .setTitle("Export all recipes") .setMessage("Export all recipes to BeerXML.") - .setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.local_storage, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { new ExportRecipes().execute(""); } }) + .setNegativeButton(R.string.drive_button, new DialogInterface.OnClickListener() { - .setNegativeButton(R.string.cancel, null); + public void onClick(DialogInterface dialog, int which) { + writeFile(DatabaseAPI.getRecipeList()); + } + + }) + .setNeutralButton(R.string.cancel, null); } private AlertDialog.Builder finishedExporting(String pathToFile) { diff --git a/src/com/biermacht/brews/frontend/fragments/RecipesFragment.java b/src/com/biermacht/brews/frontend/fragments/RecipesFragment.java index 155f269..da2b1e2 100644 --- a/src/com/biermacht/brews/frontend/fragments/RecipesFragment.java +++ b/src/com/biermacht/brews/frontend/fragments/RecipesFragment.java @@ -28,7 +28,6 @@ import com.biermacht.brews.R; import com.biermacht.brews.database.DatabaseAPI; -import com.biermacht.brews.database.DatabaseInterface; import com.biermacht.brews.frontend.AddRecipeActivity; import com.biermacht.brews.frontend.BrewTimerActivity; import com.biermacht.brews.frontend.DisplayRecipeActivity; @@ -40,11 +39,11 @@ import com.biermacht.brews.frontend.IngredientActivities.AddMiscActivity; import com.biermacht.brews.frontend.IngredientActivities.AddYeastActivity; import com.biermacht.brews.frontend.IngredientActivities.EditRecipeActivity; -import com.biermacht.brews.frontend.MainActivity; import com.biermacht.brews.frontend.adapters.DisplayRecipeCollectionPagerAdapter; import com.biermacht.brews.frontend.adapters.RecipeArrayAdapter; import com.biermacht.brews.recipe.Recipe; import com.biermacht.brews.utils.Constants; +import com.biermacht.brews.utils.DriveActivity; import com.biermacht.brews.utils.Utils; import com.biermacht.brews.utils.interfaces.BiermachtFragment; import com.biermacht.brews.xml.RecipeXmlWriter; @@ -65,9 +64,6 @@ public class RecipesFragment extends Fragment implements BiermachtFragment { private AdapterView.OnItemLongClickListener mLongClickListener; private ArrayList recipeList; - // Database Interface - private DatabaseInterface databaseInterface; - // Context menu items private ArrayList menuItems; @@ -124,9 +120,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa isTablet = true; } - // Get database Interface - databaseInterface = MainActivity.databaseInterface; - // Callback which handles the creation and operation of the contextual action bar when // a Recipe is long-pressed. mActionModeCallback = new ActionMode.Callback() { @@ -298,6 +291,10 @@ public boolean onItemLongClick(AdapterView adapterView, View view, int pos, l return pageView; } + public void recipesExportedSnackbar() { + Snackbar.make(listView, R.string.recipe_exported, Snackbar.LENGTH_LONG).show(); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -528,7 +525,7 @@ private AlertDialog.Builder exportAlert(final Recipe r) { return new AlertDialog.Builder(getActivity()) .setTitle("Export recipe") .setMessage("Export '" + r.getRecipeName() + "' to BeerXML file?") - .setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.local_storage, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { new ExportRecipe(r).execute(""); @@ -537,7 +534,17 @@ public void onClick(DialogInterface dialog, int which) { } }) - .setNegativeButton(R.string.cancel, null); + .setNeutralButton(R.string.cancel, null) + .setNegativeButton(R.string.drive_button, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int which) { + ArrayList l = new ArrayList(); + l.add(r); + ((DriveActivity) getActivity()).writeFile(l); + mActionMode.finish(); + } + + }); } private AlertDialog.Builder finishedExporting(String pathToFile) { diff --git a/src/com/biermacht/brews/utils/Constants.java b/src/com/biermacht/brews/utils/Constants.java index 437168b..53ebe67 100644 --- a/src/com/biermacht/brews/utils/Constants.java +++ b/src/com/biermacht/brews/utils/Constants.java @@ -82,7 +82,8 @@ public class Constants { public static final int REQUEST_EDIT_RECIPE = 3; public static final int REQUEST_IMPORT_FILE = 4; public static final int REQUEST_CONNECT_TO_DRIVE = 5; - public static final int REQUEST_DRIVE_FILE = 6; + public static final int REQUEST_DRIVE_FILE_OPEN = 6; + public static final int REQUEST_DRIVE_FILE_CREATE = 7; // Possible timer states public static int PAUSED = 0; diff --git a/src/com/biermacht/brews/utils/DriveActivity.java b/src/com/biermacht/brews/utils/DriveActivity.java new file mode 100644 index 0000000..274b9bd --- /dev/null +++ b/src/com/biermacht/brews/utils/DriveActivity.java @@ -0,0 +1,264 @@ +package com.biermacht.brews.utils; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; + +import com.biermacht.brews.recipe.Recipe; +import com.biermacht.brews.xml.RecipeXmlWriter; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.drive.Drive; +import com.google.android.gms.drive.DriveApi; +import com.google.android.gms.drive.DriveContents; +import com.google.android.gms.drive.MetadataChangeSet; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; + +/** + * Created by casey on 3/6/16. + */ +public abstract class DriveActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { + + public GoogleApiClient driveClient; + private ProgressDialog progressDialog; + private int action; + private ArrayList recipes; + + private static final int NONE = 0; + private static final int FILE_OPEN = 1; + private static final int FILE_WRITE = 2; + + // Callback for after creating Drive contents. + final ResultCallback driveContentsCallback = new + ResultCallback() { + @Override + public void onResult(DriveApi.DriveContentsResult result) { + + if (! result.getStatus().isSuccess()) { + // TODO HANDLE ERROR + return; + } + final DriveContents driveContents = result.getDriveContents(); + + // Write content to DriveContents + OutputStream outputStream = driveContents.getOutputStream(); + Writer writer = new OutputStreamWriter(outputStream); + Recipe r = new Recipe(); + try { + writer.write(new RecipeXmlWriter(getApplicationContext()).getXmlText(recipes)); + writer.close(); + } catch (IOException e) { + Log.e("MainActivity", e.getMessage()); + } + + // Determine the default title for the file. + String title = RecipeXmlWriter.generateFileName("all-recipes-"); + if (recipes.size() == 1) { + title = RecipeXmlWriter.generateFileName(recipes.get(0).getRecipeName() + "-"); + } + + // Start intent to pick and create file. + MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder() + .setTitle(title) + .setMimeType("text/xml").build(); + IntentSender intentSender = Drive.DriveApi + .newCreateFileActivityBuilder() + .setInitialMetadata(metadataChangeSet) + .setInitialDriveContents(result.getDriveContents()) + .setActivityTitle("Save Recipe(s)") + .build(driveClient); + try { + startIntentSenderForResult(intentSender, + Constants.REQUEST_DRIVE_FILE_CREATE, + null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + // TODO Handle the exception + } + } + }; + + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + // Create the progress dialog view - displayed when connecting to Google APIs. + progressDialog = new ProgressDialog(this); + progressDialog.setMessage("Connecting to Google APIs..."); + progressDialog.setIndeterminate(false); + progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + progressDialog.setCancelable(false); + + // Create the connection driveClient. + driveClient = new GoogleApiClient.Builder(this) + .addApi(Drive.API) + .addScope(Drive.SCOPE_FILE) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .build(); + } + + // Abstract methods to be overridden by the subclass. These handle drive events. + public abstract void onDriveFilePicked(Intent data); + + public abstract void onDriveFileWritten(Intent data); + + public void connectToDrive() { + driveClient.connect(); + progressDialog.show(); + } + + @Override + protected void onStop() { + driveClient.disconnect(); + super.onStop(); + } + + public void pickFile() { + if (! driveClient.isConnected()) { + this.action = FILE_OPEN; + connectToDrive(); + } + else { + _pickFile(); + } + } + + private void _pickFile() { + Log.d("DriveActivity", "Starting Google Drive file picker intent"); + this.action = NONE; + IntentSender i = Drive.DriveApi + .newOpenFileActivityBuilder() + .build(driveClient); + try { + this.startIntentSenderForResult(i, Constants.REQUEST_DRIVE_FILE_OPEN, null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + e.printStackTrace(); + } + } + + public void writeFile(ArrayList recipes) { + // Store the given recipes so we can write them later. + this.recipes = recipes; + + if (! driveClient.isConnected()) { + this.action = FILE_WRITE; + connectToDrive(); + } + else { + _writeFile(); + } + } + + private void _writeFile() { + Log.d("DriveActivity", "Writing file to Google Drive."); + this.action = NONE; + IntentSender i = Drive.DriveApi + .newOpenFileActivityBuilder() + .build(driveClient); + + Drive.DriveApi.newDriveContents(driveClient) + .setResultCallback(driveContentsCallback); + } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + + switch (requestCode) { + case Constants.REQUEST_CONNECT_TO_DRIVE: + Log.d("DriveActivity", "Result from Google API: " + resultCode); + handleConnectToDrive(resultCode, data); + break; + case Constants.REQUEST_DRIVE_FILE_OPEN: + Log.d("MainActivity", "Result from Google Drive file access: " + resultCode); + handleDriveFileOpen(resultCode, data); + break; + case Constants.REQUEST_DRIVE_FILE_CREATE: + Log.d("DriveActivity", "Result from Google Drive file create: " + resultCode); + handleDriveFileWritten(resultCode, data); + break; + default: + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + + private void handleConnectToDrive(int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + // Make sure the app is not already connected or attempting to connect + if (! this.driveClient.isConnecting() && + ! this.driveClient.isConnected()) { + this.driveClient.connect(); + } + } + } + + private void handleDriveFileOpen(int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + // Pass to the onFilePicked method for the subclass to handle. + this.onDriveFilePicked(data); + } + } + + private void handleDriveFileWritten(int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + // Pass to the onDriveFileWritten method for the subclass to handle. + this.onDriveFileWritten(data); + } + + // Clear recipes. + this.recipes = null; + } + + @Override + public void onConnected(Bundle bundle) { + Log.d("DriveApiHelper", "Connected to Google APIs"); + if (progressDialog.isShowing()) { + Log.d("DriveApiHelper", "Dismissing progress dialog"); + progressDialog.dismiss(); + } + + // Based on the action, call the correct method. + switch (this.action) { + case FILE_OPEN: + _pickFile(); + break; + case FILE_WRITE: + _writeFile(); + break; + default: + Log.e("DriveApiHelper", "Unrecognized action: " + this.action); + break; + } + } + + @Override + public void onConnectionSuspended(int i) { + + } + + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.d("MainActivity", "Google API Connection failed"); + if (result.hasResolution()) { + try { + result.startResolutionForResult(this, Constants.REQUEST_CONNECT_TO_DRIVE); + } catch (IntentSender.SendIntentException e) { + driveClient.connect(); + } + } + else { + GoogleApiAvailability.getInstance().getErrorDialog(this, result.getErrorCode(), + Constants.REQUEST_CONNECT_TO_DRIVE).show(); + } + + } +} diff --git a/src/com/biermacht/brews/xml/RecipeXmlWriter.java b/src/com/biermacht/brews/xml/RecipeXmlWriter.java index 3259225..9aa7f5f 100644 --- a/src/com/biermacht/brews/xml/RecipeXmlWriter.java +++ b/src/com/biermacht/brews/xml/RecipeXmlWriter.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; +import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -51,95 +52,148 @@ public void writeRecipe(Recipe r, String filePrefix) { this.writeRecipes(list, filePrefix); } - public void writeRecipes(List list, String filePrefix) { + public Document generateDocument(List list) throws ParserConfigurationException { // Open the document. DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder; - try { - // This throws the exceptions! - docBuilder = docFactory.newDocumentBuilder(); - - // Create root element. - Document doc = docBuilder.newDocument(); - Element rootElement = doc.createElement("RECIPES"); - doc.appendChild(rootElement); - - for (Recipe r : list) { - // Element for this recipe. - Element recipeElement = doc.createElement("RECIPE"); - - // Create a mapping of name -> value - Map map = new HashMap(); - map.put("NAME", r.getRecipeName()); - map.put("VERSION", r.getVersion() + ""); - map.put("TYPE", r.getType()); - map.put("EQUIPMENT", ""); - map.put("BREWER", ""); - map.put("BATCH_SIZE", r.getBeerXmlStandardBatchSize() + ""); - map.put("BOIL_SIZE", r.getBeerXmlStandardBoilSize() + ""); - map.put("BOIL_TIME", r.getBoilTime() + ""); - map.put("EFFICIENCY", r.getEfficiency() + ""); - map.put("NOTES", r.getNotes()); - map.put("OG", r.getOG() + ""); - map.put("FG", r.getFG() + ""); - map.put("DISPLAY_OG", r.getMeasuredOG() + ""); - map.put("DISPLAY_FG", r.getMeasuredFG() + ""); - map.put("FERMENTATION_STAGES", r.getFermentationStages() + ""); - map.put("PRIMARY_AGE", r.getFermentationAge(Recipe.STAGE_PRIMARY) + ""); - map.put("SECONDARY_AGE", r.getFermentationAge(Recipe.STAGE_SECONDARY) + ""); - map.put("TERTIARY_AGE", r.getFermentationAge(Recipe.STAGE_TERTIARY) + ""); - map.put("PRIMARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_PRIMARY) + ""); - map.put("SECONDARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_SECONDARY) + ""); - map.put("TERTIARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_TERTIARY) + ""); - map.put("AGE", r.getBottleAge() + ""); - - for (Map.Entry e : map.entrySet()) { - String fieldName = e.getKey(); - String fieldValue = e.getValue(); - Element element = doc.createElement(fieldName); - element.setTextContent(fieldValue); - recipeElement.appendChild(element); - } - - // Add elements to recipe. - recipeElement.appendChild(this.getHopsChild(doc, r.getHopsList())); - recipeElement.appendChild(this.getFermentablesChild(doc, r.getFermentablesList())); - recipeElement.appendChild(this.getMiscsChild(doc, r.getMiscList())); - recipeElement.appendChild(this.getYeastsChild(doc, r.getYeastsList())); - recipeElement.appendChild(this.getWatersChild(doc, r.getWatersList())); - recipeElement.appendChild(this.getMashChild(doc, r.getMashProfile())); - recipeElement.appendChild(this.getStyleChild(doc, r.getStyle())); - - // Add recipe to root element. - rootElement.appendChild(recipeElement); + + // This throws the exceptions! + docBuilder = docFactory.newDocumentBuilder(); + + // Create root element. + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("RECIPES"); + doc.appendChild(rootElement); + + for (Recipe r : list) { + // Element for this recipe. + Element recipeElement = doc.createElement("RECIPE"); + + // Create a mapping of name -> value + Map map = new HashMap(); + map.put("NAME", r.getRecipeName()); + map.put("VERSION", r.getVersion() + ""); + map.put("TYPE", r.getType()); + map.put("EQUIPMENT", ""); + map.put("BREWER", ""); + map.put("BATCH_SIZE", r.getBeerXmlStandardBatchSize() + ""); + map.put("BOIL_SIZE", r.getBeerXmlStandardBoilSize() + ""); + map.put("BOIL_TIME", r.getBoilTime() + ""); + map.put("EFFICIENCY", r.getEfficiency() + ""); + map.put("NOTES", r.getNotes()); + map.put("OG", r.getOG() + ""); + map.put("FG", r.getFG() + ""); + map.put("DISPLAY_OG", r.getMeasuredOG() + ""); + map.put("DISPLAY_FG", r.getMeasuredFG() + ""); + map.put("FERMENTATION_STAGES", r.getFermentationStages() + ""); + map.put("PRIMARY_AGE", r.getFermentationAge(Recipe.STAGE_PRIMARY) + ""); + map.put("SECONDARY_AGE", r.getFermentationAge(Recipe.STAGE_SECONDARY) + ""); + map.put("TERTIARY_AGE", r.getFermentationAge(Recipe.STAGE_TERTIARY) + ""); + map.put("PRIMARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_PRIMARY) + ""); + map.put("SECONDARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_SECONDARY) + ""); + map.put("TERTIARY_TEMP", r.getBeerXmlStandardFermentationTemp(Recipe.STAGE_TERTIARY) + ""); + map.put("AGE", r.getBottleAge() + ""); + + for (Map.Entry e : map.entrySet()) { + String fieldName = e.getKey(); + String fieldValue = e.getValue(); + Element element = doc.createElement(fieldName); + element.setTextContent(fieldValue); + recipeElement.appendChild(element); } - // Write to XML file. - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - DOMSource source = new DOMSource(doc); + // Add elements to recipe. + recipeElement.appendChild(this.getHopsChild(doc, r.getHopsList())); + recipeElement.appendChild(this.getFermentablesChild(doc, r.getFermentablesList())); + recipeElement.appendChild(this.getMiscsChild(doc, r.getMiscList())); + recipeElement.appendChild(this.getYeastsChild(doc, r.getYeastsList())); + recipeElement.appendChild(this.getWatersChild(doc, r.getWatersList())); + recipeElement.appendChild(this.getMashChild(doc, r.getMashProfile())); + recipeElement.appendChild(this.getStyleChild(doc, r.getStyle())); + + // Add recipe to root element. + rootElement.appendChild(recipeElement); + } + return doc; + } + + public String getXmlText(List list) { + // Get the generated XML doc. + Document doc; + try { + doc = generateDocument(list); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + return null; + } - // Generate date string - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); - String dateString = dateFormat.format(new Date()); + DOMSource domSource = new DOMSource(doc); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = null; - // Create file object - File file = getStoragePath(filePrefix + dateString + ".xml"); - StreamResult result = new StreamResult(file); - Log.d("WriteXmlFile", "Writing XML to:" + file); + try { + transformer = tf.newTransformer(); + } catch (TransformerConfigurationException e) { + e.printStackTrace(); + } - transformer.transform(source, result); - this.lastFileLocation = file.getAbsolutePath(); + try { + transformer.transform(domSource, result); + } catch (TransformerException e) { + e.printStackTrace(); + } + + writer.flush(); + return writer.toString(); + } + public static String generateFileName(String prefix) { + // Generate date string + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); + String dateString = dateFormat.format(new Date()); + + return prefix + dateString + ".xml"; + } + + public void writeRecipes(List list, String filePrefix) { + // Get the generated XML doc. + Document doc; + try { + doc = generateDocument(list); } catch (ParserConfigurationException e) { e.printStackTrace(); + return; + } + + // Write to XML file. + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = null; + try { + transformer = transformerFactory.newTransformer(); } catch (TransformerConfigurationException e) { e.printStackTrace(); + return; + } + DOMSource source = new DOMSource(doc); + + // Create file object + File file = null; + try { + file = getStoragePath(generateFileName(filePrefix)); } catch (IOException e) { e.printStackTrace(); + } + StreamResult result = new StreamResult(file); + Log.d("WriteXmlFile", "Writing XML to:" + file); + + try { + transformer.transform(source, result); } catch (TransformerException e) { e.printStackTrace(); } + this.lastFileLocation = file.getAbsolutePath(); } /**