net.potterpcs.recipebook.ImporterActivity.java Source code

Java tutorial

Introduction

Here is the source code for net.potterpcs.recipebook.ImporterActivity.java

Source

/*
 *    Copyright 2012 Michael Potter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

/*
 * ImporterActivity.java - Import external recipes into the app database,
 * allowing the user to choose which recipes are imported. 
 */

package net.potterpcs.recipebook;

import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import net.potterpcs.recipebook.RecipeData.Recipe;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import android.app.ListActivity;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;

public class ImporterActivity extends ListActivity {
    // Tag for logging
    //   private static final String TAG = "ImporterActivity";

    // Bundle extra that holds the imported file's location.
    // This isn't really used yet.
    public static final String IMPORT_PATH_EXTRA = "import-path";

    // Handle to the data layer
    RecipeData data;

    // The list of recipes to import (will be filled by the worker thread)
    ArrayList<Recipe> importedRecipes;

    // The names of all the recipes in the imported batch
    String[] recipeNames;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        data = ((RecipeBook) getApplication()).getData();

        // Read the location from the Bundle's state, if any, or get it
        // straight from the Intent.
        String path;
        if (savedInstanceState != null) {
            path = savedInstanceState.getString(IMPORT_PATH_EXTRA);
        } else {
            path = getIntent().getDataString();
        }

        // All of the work will be done by the worker thread,
        // because the recipebook file may be online, and 3.0+
        // apps can't access the network in the UI thread.
        try {
            importedRecipes = new DownloadHelperTask(this).execute(path).get();
        } catch (InterruptedException e) {
            importedRecipes = new ArrayList<Recipe>();
        } catch (ExecutionException e) {
            importedRecipes = new ArrayList<Recipe>();
        }

    }

    public void onImportButton(View v) {
        // We can't simply use getCheckedItemIds(), because it only works
        // if the ListAdapter has stable IDs, and ArrayAdapters don't.
        // So, we have to use getCheckedItemPositions instead, and loop
        // through the array.
        SparseBooleanArray sba = getListView().getCheckedItemPositions();
        ArrayList<Recipe> selectedRecipes = new ArrayList<Recipe>();
        for (int i = 0; i < sba.size(); i++) {
            if (sba.get(i)) {
                selectedRecipes.add(importedRecipes.get(i));
            }
        }
        data.insertImportedRecipes(selectedRecipes);
        finish();
    }

    public void onSelectAllButton(View v) {
        // Run through the list of recipes, and check each one
        for (int i = 0; i < getListView().getCount(); i++) {
            getListView().setItemChecked(i, true);
        }
    }

    public void onClearAllButton(View v) {
        // Run through the list of recipes, and uncheck each one
        for (int i = 0; i < getListView().getCount(); i++) {
            getListView().setItemChecked(i, false);
        }
    }

    // The class representing the worker thread
    private class DownloadHelperTask extends AsyncTask<String, Void, ArrayList<Recipe>> {
        // Handle to the parent activity
        private ListActivity activity;

        public DownloadHelperTask(ListActivity a) {
            activity = a;
        }

        @Override
        protected ArrayList<Recipe> doInBackground(String... params) {
            RecipeData appData = ((RecipeBook) getApplication()).getData();
            ArrayList<Recipe> importedRecipes = new ArrayList<Recipe>();
            String path = params[0];

            // Parse the path parameter as a URI. If it's a "file:///" URI,
            // then we're loading from the device, and we can use the simpler
            // method offered by RecipeData. Otherwise, we have to load from
            // the network.
            Uri uriPath = Uri.parse(path);
            if (uriPath.getScheme().startsWith("file")) {
                try {
                    importedRecipes = appData.importRecipes(path);
                } catch (IOException e) {
                    // Not much we can do if there's an I/O exception.
                    // TODO Maybe pop up a dialog?
                    e.printStackTrace();
                }
            } else {
                // Set up the networking stuff
                AndroidHttpClient client = AndroidHttpClient.newInstance("A to Z Recipes for Android");
                HttpGet request = new HttpGet(path);
                HttpParams httpPars = new BasicHttpParams();
                HttpConnectionParams.setSoTimeout(httpPars, 60000);
                request.setParams(httpPars);
                HttpResponse response;

                // Recipebook batches are JSON files, but we'll let the data
                // layer handle that. All we do here is read the batch into
                // a string, then hand that off to RecipeData.
                String json;
                try {
                    response = client.execute(request);
                    byte[] file = EntityUtils.toByteArray(response.getEntity());
                    json = new String(file);
                } catch (IOException e) {
                    // TODO logging
                    json = null;
                }
                importedRecipes = appData.parseJsonRecipes(json);
                client.close();
            }

            return importedRecipes;
        }

        @Override
        protected void onPostExecute(ArrayList<Recipe> result) {
            // Here we set up the UI stuff, making a list of the recipes'
            // names, and creating a checkable ListView to hold them.
            ArrayList<String> als = new ArrayList<String>();
            for (Recipe r : importedRecipes) {
                als.add(r.name);
            }
            recipeNames = (String[]) als.toArray(new String[als.size()]);

            activity.setContentView(R.layout.importer);
            ListAdapter adapter = new ArrayAdapter<String>(activity, android.R.layout.simple_list_item_checked,
                    android.R.id.text1, recipeNames);

            activity.setListAdapter(adapter);

        }
    }
}