com.radicaldynamic.groupinform.activities.AccountFolderList.java Source code

Java tutorial

Introduction

Here is the source code for com.radicaldynamic.groupinform.activities.AccountFolderList.java

Source

/*
 * Copyright (C) 2009 University of Washington
 * 
 * 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.
 */

package com.radicaldynamic.groupinform.activities;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;

import com.radicaldynamic.groupinform.R;
import com.radicaldynamic.groupinform.adapters.AccountFolderListAdapter;
import com.radicaldynamic.groupinform.application.Collect;
import com.radicaldynamic.groupinform.listeners.SynchronizeFoldersListener;
import com.radicaldynamic.groupinform.logic.AccountDevice;
import com.radicaldynamic.groupinform.logic.AccountFolder;
import com.radicaldynamic.groupinform.logic.InformOnlineState;
import com.radicaldynamic.groupinform.tasks.SynchronizeFoldersTask;
import com.radicaldynamic.groupinform.utilities.HttpUtils;
import com.radicaldynamic.groupinform.utilities.FileUtilsExtended;

/*
 * Interface used to switch between folders and select destination
 * folders to copy and/or move forms and/or data to.
 */
public class AccountFolderList extends ListActivity implements SynchronizeFoldersListener {
    private static final String t = "AccountFolderList: ";

    private static final int MENU_ADD = Menu.FIRST;
    private static final int MENU_SYNC_LIST = Menu.FIRST + 1;

    private static final int CONTEXT_MENU_EDIT = Menu.FIRST;
    private static final int CONTEXT_MENU_INFO = Menu.FIRST + 1;

    public static final int DIALOG_DENIED_NOT_OWNER = 1;
    public static final int DIALOG_MORE_INFO = 2;

    // Intent keys
    public static final String KEY_COPY_TO_FOLDER = "key_copytofolder";
    public static final String KEY_FOLDER_ID = "key_folderid";
    public static final String KEY_FOLDER_NAME = "key_foldername";

    private RefreshViewTask mRefreshViewTask;
    private SynchronizeFoldersTask mSynchronizeFoldersTask;

    private AccountFolder mFolder;
    private String mSelectedFolderId;
    private String mSelectedFolderName;
    private boolean mCopyToFolder = false;

    private ProgressDialog mProgressDialog;

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

        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

        setContentView(R.layout.generic_list);

        if (savedInstanceState == null) {
            Intent intent = getIntent();

            if (intent == null) {
                // Load defaults
            } else {
                mCopyToFolder = intent.getBooleanExtra(KEY_COPY_TO_FOLDER, false);
            }
        } else {
            if (savedInstanceState.containsKey(KEY_COPY_TO_FOLDER))
                mCopyToFolder = savedInstanceState.getBoolean(KEY_COPY_TO_FOLDER);

            if (savedInstanceState.containsKey(KEY_FOLDER_ID))
                mSelectedFolderId = savedInstanceState.getString(KEY_FOLDER_ID);

            if (savedInstanceState.containsKey(KEY_FOLDER_NAME))
                mSelectedFolderName = savedInstanceState.getString(KEY_FOLDER_NAME);

            Object data = getLastNonConfigurationInstance();

            if (data instanceof RefreshViewTask)
                mRefreshViewTask = (RefreshViewTask) data;
            else if (data instanceof SynchronizeFoldersTask)
                mSynchronizeFoldersTask = (SynchronizeFoldersTask) data;
        }

        if (mCopyToFolder)
            setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_copy_to_folder));
        else
            setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_form_folders));
    }

    @Override
    protected void onDestroy() {
        if (mProgressDialog != null && mProgressDialog.isShowing())
            mProgressDialog.dismiss();

        // Clean up folder synchronization task
        if (mSynchronizeFoldersTask != null) {
            mSynchronizeFoldersTask.setListener(null);

            if (mSynchronizeFoldersTask.getStatus() == AsyncTask.Status.FINISHED) {
                mSynchronizeFoldersTask.cancel(true);
            }
        }

        super.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Handle resume of folder synchronization task
        if (mSynchronizeFoldersTask != null) {
            mSynchronizeFoldersTask.setListener(this);

            if (mSynchronizeFoldersTask != null) {
                if (mSynchronizeFoldersTask.getStatus() == AsyncTask.Status.RUNNING) {
                    synchronizationHandler(null);
                } else if (mSynchronizeFoldersTask.getStatus() == AsyncTask.Status.FINISHED) {
                    if (mProgressDialog != null && mProgressDialog.isShowing()) {
                        mProgressDialog.dismiss();
                    }
                }
            }
        }

        loadScreen();
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        boolean enabled = false;
        boolean visible = true;

        if (Collect.getInstance().getIoService().isSignedIn())
            enabled = true;

        if (mCopyToFolder)
            visible = false;

        if (!Collect.getInstance().getInformOnlineState().getDeviceRole().equals(AccountDevice.ROLE_DATA_ENTRY)) {
            menu.add(0, CONTEXT_MENU_EDIT, 0, getString(R.string.tf_edit_folder)).setEnabled(enabled)
                    .setVisible(visible);
        }

        menu.add(0, CONTEXT_MENU_INFO, 0, getString(R.string.tf_more_info));
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.app.Activity#onCreateDialog(int)
     */
    @Override
    protected Dialog onCreateDialog(int id) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        Dialog dialog = null;

        switch (id) {
        case DIALOG_DENIED_NOT_OWNER:
            builder.setIcon(R.drawable.ic_dialog_info).setTitle(R.string.tf_unable_to_edit_folder_not_owner_title)
                    .setMessage(R.string.tf_unable_to_edit_folder_not_owner_msg)
                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });

            dialog = builder.create();
            break;

        case DIALOG_MORE_INFO:
            String message = "Owner:\n" + mFolder.getOwnerAlias() + "\n\n";
            message = message + "Description:\n" + mFolder.getDescription();

            builder.setIcon(R.drawable.ic_dialog_info).setTitle(mFolder.getName()).setMessage(message)
                    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            removeDialog(DIALOG_MORE_INFO);
                        }
                    });

            dialog = builder.create();
            break;
        }

        return dialog;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        boolean enabled = false;
        boolean visible = true;

        if (Collect.getInstance().getIoService().isSignedIn())
            enabled = true;

        if (mCopyToFolder)
            visible = false;

        if (!Collect.getInstance().getInformOnlineState().getDeviceRole().equals(AccountDevice.ROLE_DATA_ENTRY)) {
            menu.add(0, MENU_ADD, 0, getString(R.string.tf_create_folder)).setIcon(R.drawable.ic_menu_add)
                    .setEnabled(enabled).setVisible(visible);
        }

        menu.add(0, MENU_SYNC_LIST, 0, getString(R.string.tf_replication_list))
                .setIcon(R.drawable.ic_menu_sync_list).setEnabled(enabled).setVisible(visible);

        return true;
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        AccountFolder folder = (AccountFolder) getListView().getItemAtPosition(info.position);

        switch (item.getItemId()) {
        case CONTEXT_MENU_EDIT:
            if (Collect.getInstance().getInformOnlineState().getDeviceId().equals(folder.getOwnerId())) {
                Intent i = new Intent(this, AccountFolderActivity.class);
                i.putExtra(AccountFolderActivity.KEY_FOLDER_ID, folder.getId());
                i.putExtra(AccountFolderActivity.KEY_FOLDER_REV, folder.getRev());
                i.putExtra(AccountFolderActivity.KEY_FOLDER_OWNER, folder.getOwnerId());
                i.putExtra(AccountFolderActivity.KEY_FOLDER_NAME, folder.getName());
                i.putExtra(AccountFolderActivity.KEY_FOLDER_DESC, folder.getDescription());
                i.putExtra(AccountFolderActivity.KEY_FOLDER_VISIBILITY, folder.getVisibility());
                startActivity(i);
            } else {
                showDialog(DIALOG_DENIED_NOT_OWNER);
            }

            break;

        case CONTEXT_MENU_INFO:
            mFolder = folder;
            showDialog(DIALOG_MORE_INFO);
            break;

        default:
            return super.onContextItemSelected(item);
        }

        return true;
    }

    /**
     * Stores the path of selected form and finishes.
     */
    @Override
    protected void onListItemClick(ListView listView, View view, int position, long id) {
        mFolder = (AccountFolder) getListAdapter().getItem(position);

        // Store separately because mFolder may not be available after activity restart
        mSelectedFolderId = mFolder.getId();
        mSelectedFolderName = mFolder.getName();

        if (mFolder.isReplicated() && !Collect.getInstance().getDbService().isDbLocal(mFolder.getId())) {
            mSynchronizeFoldersTask = new SynchronizeFoldersTask();
            mSynchronizeFoldersTask.setListener(AccountFolderList.this);
            mSynchronizeFoldersTask.setTransferMode(SynchronizeFoldersListener.MODE_PULL);
            mSynchronizeFoldersTask.setFolders(new ArrayList<String>(Collections.singletonList(mFolder.getId())));
            mSynchronizeFoldersTask.execute();
        } else {
            openFolder();
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_ADD:
            startActivity(new Intent(this, AccountFolderActivity.class)
                    .putExtra(AccountFolderActivity.KEY_NEW_FOLDER, true));
            break;

        case MENU_SYNC_LIST:
            startActivity(new Intent(this, AccountFolderReplicationList.class));
            break;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mRefreshViewTask != null && mRefreshViewTask.getStatus() != AsyncTask.Status.FINISHED)
            return mRefreshViewTask;

        if (mSynchronizeFoldersTask != null && mSynchronizeFoldersTask.getStatus() != AsyncTask.Status.FINISHED)
            return mSynchronizeFoldersTask;

        return null;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(KEY_COPY_TO_FOLDER, mCopyToFolder);
        outState.putString(KEY_FOLDER_ID, mSelectedFolderId);
        outState.putString(KEY_FOLDER_NAME, mSelectedFolderName);
    }

    /*
     * Refresh the main form browser view as requested by the user
     */
    private class RefreshViewTask extends AsyncTask<Void, Void, Void> {
        private ArrayList<AccountFolder> folders = new ArrayList<AccountFolder>();

        @Override
        protected Void doInBackground(Void... nothing) {
            if (FileUtilsExtended.isFileOlderThan(
                    getCacheDir() + File.separator + FileUtilsExtended.FOLDER_CACHE_FILE,
                    FileUtilsExtended.TIME_TWO_MINUTES))
                fetchFolderList(false);

            folders = loadFolderList();

            return null;
        }

        @Override
        protected void onPreExecute() {
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected void onPostExecute(Void nothing) {
            RelativeLayout onscreenProgress = (RelativeLayout) findViewById(R.id.progress);
            onscreenProgress.setVisibility(View.GONE);

            if (folders.isEmpty()) {
                TextView nothingToDisplay = (TextView) findViewById(R.id.nothingToDisplay);
                nothingToDisplay.setVisibility(View.VISIBLE);
            } else {
                AccountFolderListAdapter adapter;

                adapter = new AccountFolderListAdapter(getApplicationContext(), R.layout.folder_list_item, folders);

                setListAdapter(adapter);
            }

            setProgressBarIndeterminateVisibility(false);
        }
    }

    /*
     * Fetch a new folder list from Inform Online and store it on disk
     */
    public static void fetchFolderList(boolean fetchAnyway) {
        if (Collect.getInstance().getIoService().isSignedIn() || fetchAnyway) {
            if (Collect.Log.DEBUG)
                Log.d(Collect.LOGTAG, t + "fetching list of folders");
        } else {
            if (Collect.Log.DEBUG)
                Log.d(Collect.LOGTAG, t + "not signed in, skipping folder list fetch");
            return;
        }

        // Try to ping the service to see if it is "up"
        String folderListUrl = Collect.getInstance().getInformOnlineState().getServerUrl() + "/folder/list";
        String getResult = HttpUtils.getUrlData(folderListUrl);
        JSONObject jsonFolderList;

        try {
            jsonFolderList = (JSONObject) new JSONTokener(getResult).nextValue();

            String result = jsonFolderList.optString(InformOnlineState.RESULT, InformOnlineState.ERROR);

            if (result.equals(InformOnlineState.OK)) {
                // Write out list of jsonFolders for later retrieval by loadFoldersList()
                JSONArray jsonFolders = jsonFolderList.getJSONArray("folders");

                try {
                    // Write out a folder list cache file
                    FileOutputStream fos = new FileOutputStream(
                            new File(Collect.getInstance().getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE));
                    fos.write(jsonFolders.toString().getBytes());
                    fos.close();
                } catch (Exception e) {
                    if (Collect.Log.ERROR)
                        Log.e(Collect.LOGTAG, t + "unable to write folder cache: " + e.toString());
                    e.printStackTrace();
                }
            } else {
                // There was a problem.. handle it!
            }
        } catch (NullPointerException e) {
            // Communication error
            if (Collect.Log.ERROR)
                Log.e(Collect.LOGTAG, t + "no getResult to parse.  Communication error with node.js server?");
            e.printStackTrace();
        } catch (JSONException e) {
            // Parse error (malformed result)
            if (Collect.Log.ERROR)
                Log.e(Collect.LOGTAG, t + "failed to parse getResult " + getResult);
            e.printStackTrace();
        }
    }

    public static ArrayList<AccountFolder> loadFolderList() {
        if (Collect.Log.DEBUG)
            Log.d(Collect.LOGTAG, t + "loading folder cache");

        ArrayList<AccountFolder> folders = new ArrayList<AccountFolder>();

        if (!new File(Collect.getInstance().getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE).exists()) {
            if (Collect.Log.WARN)
                Log.w(Collect.LOGTAG, t + "folder cache file cannot be read: aborting loadFolderList()");
            return folders;
        }

        try {
            FileInputStream fis = new FileInputStream(
                    new File(Collect.getInstance().getCacheDir(), FileUtilsExtended.FOLDER_CACHE_FILE));
            InputStreamReader reader = new InputStreamReader(fis);
            BufferedReader buffer = new BufferedReader(reader, 8192);
            StringBuilder sb = new StringBuilder();

            String cur;

            while ((cur = buffer.readLine()) != null) {
                sb.append(cur + "\n");
            }

            buffer.close();
            reader.close();
            fis.close();

            try {
                JSONArray jsonFolders = (JSONArray) new JSONTokener(sb.toString()).nextValue();

                for (int i = 0; i < jsonFolders.length(); i++) {
                    JSONObject jsonFolder = jsonFolders.getJSONObject(i);

                    AccountFolder folder = new AccountFolder(jsonFolder.getString("id"),
                            jsonFolder.getString("rev"), jsonFolder.getString("owner"),
                            jsonFolder.getString("name"), jsonFolder.getString("description"),
                            jsonFolder.getString("visibility"), jsonFolder.getBoolean("replication"));

                    folders.add(folder);

                    // Also update the account folder hash since this will be needed by BrowserActivity, among other things
                    Collect.getInstance().getInformOnlineState().getAccountFolders().put(folder.getId(), folder);
                }
            } catch (JSONException e) {
                // Parse error (malformed result)
                if (Collect.Log.ERROR)
                    Log.e(Collect.LOGTAG, t + "failed to parse JSON " + sb.toString());
                e.printStackTrace();
            }
        } catch (Exception e) {
            if (Collect.Log.ERROR)
                Log.e(Collect.LOGTAG, t + "unable to read folder cache: " + e.toString());
            e.printStackTrace();
        }

        return folders;
    }

    @Override
    public void synchronizationHandler(Message msg) {
        if (msg == null) {
            // Close any existing progress dialogs
            if (mProgressDialog != null && mProgressDialog.isShowing())
                mProgressDialog.dismiss();

            // Start new dialog with suitable message
            mProgressDialog = new ProgressDialog(AccountFolderList.this);
            mProgressDialog.setMessage(getString(R.string.tf_synchronizing_folders_dialog_msg));
            mProgressDialog.show();
        } else {
            // Update progress dialog
            if (mProgressDialog != null && mProgressDialog.isShowing()) {
                mProgressDialog.setMessage(
                        getString(R.string.tf_synchronizing_folder_count_dialog_msg, msg.arg1, msg.arg2));
            }
        }
    }

    @Override
    public void synchronizationTaskFinished(Bundle data) {
        if (mProgressDialog != null && mProgressDialog.isShowing())
            mProgressDialog.dismiss();

        if (data.getBoolean(SynchronizeFoldersListener.SUCCESSFUL)) {
            openFolder();
        } else {
            Toast.makeText(getApplicationContext(),
                    "Unable to open " + mSelectedFolderName + ". Please try again in a few minutes.",
                    Toast.LENGTH_LONG).show();
        }
    }

    /**
     * Load the various elements of the screen that must wait for other tasks to
     * complete
     */
    private void loadScreen() {
        mRefreshViewTask = new RefreshViewTask();
        mRefreshViewTask.execute();

        registerForContextMenu(getListView());

        if (mCopyToFolder) {
            Toast.makeText(getApplicationContext(), "Select destination folder", Toast.LENGTH_SHORT).show();
        }
    }

    private void openFolder() {
        setProgressBarIndeterminateVisibility(true);

        if (mCopyToFolder) {
            Intent i = new Intent();
            i.putExtra(KEY_FOLDER_NAME, mFolder.getName());
            i.putExtra(KEY_FOLDER_ID, mFolder.getId());
            setResult(RESULT_OK, i);
            finish();
        } else {
            Collect.getInstance().getInformOnlineState().setSelectedDatabase(mSelectedFolderId);
            finish();
        }
    }
}