/*******************************************************************************
* This file is part of Zandy.
*
* Zandy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Zandy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Zandy. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package com.gimranov.zandy.app;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.util.Base64;
import android.util.Log;
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.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TextView.BufferType;
import android.widget.Toast;
import com.crashlytics.android.Crashlytics;
import com.gimranov.zandy.app.data.Attachment;
import com.gimranov.zandy.app.data.Database;
import com.gimranov.zandy.app.data.Item;
import com.gimranov.zandy.app.task.APIRequest;
import com.gimranov.zandy.app.task.ZoteroAPITask;
import com.gimranov.zandy.app.webdav.WebDavTrust;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.json.JSONException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* This Activity handles displaying and editing attachments. It works almost the same as
* ItemDataActivity and TagActivity, using a simple ArrayAdapter on Bundles with the creator info.
*
* This currently operates by showing the attachments for a given item
*
* @author ajlyon
*/
public class AttachmentActivity extends ListActivity {
private static final String TAG = "com.gimranov.zandy.app.AttachmentActivity";
static final int DIALOG_CONFIRM_NAVIGATE = 4;
static final int DIALOG_FILE_PROGRESS = 6;
static final int DIALOG_CONFIRM_DELETE = 5;
static final int DIALOG_NOTE = 3;
static final int DIALOG_NEW = 1;
public Item item;
private ProgressDialog mProgressDialog;
private ProgressThread progressThread;
private Database db;
/**
* For <= Android 2.1 (API 7), we can't pass bundles to showDialog(), so set this instead
*/
private Bundle b = new Bundle();
private ArrayList<File> tmpFiles;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tmpFiles = new ArrayList<File>();
db = new Database(this);
/* Get the incoming data from the calling activity */
final String itemKey = getIntent().getStringExtra("com.gimranov.zandy.app.itemKey");
Item item = Item.load(itemKey, db);
this.item = item;
if (item == null) {
Log.e(TAG, "AttachmentActivity started without itemKey; finishing.");
finish();
return;
}
this.setTitle(getResources().getString(R.string.attachments_for_item, item.getTitle()));
ArrayList<Attachment> rows = Attachment.forItem(item, db);
/*
* We use the standard ArrayAdapter, passing in our data as a Attachment.
* Since it's no longer a simple TextView, we need to override getView, but
* we can do that anonymously.
*/
setListAdapter(new ArrayAdapter<Attachment>(this, R.layout.list_attach, rows) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row;
// We are reusing views, but we need to initialize it if null
if (null == convertView) {
LayoutInflater inflater = getLayoutInflater();
row = inflater.inflate(R.layout.list_attach, null);
} else {
row = convertView;
}
ImageView tvType = (ImageView) row.findViewById(R.id.attachment_type);
TextView tvSummary = (TextView) row.findViewById(R.id.attachment_summary);
Attachment att = getItem(position);
Log.d(TAG, "Have an attachment: " + att.title + " fn:" + att.filename + " status:" + att.status);
tvType.setImageResource(Item.resourceForType(att.getType()));
try {
Log.d(TAG, att.content.toString(4));
} catch (JSONException e) {
Log.e(TAG, "JSON parse exception when reading attachment content", e);
}
if (att.getType().equals("note")) {
String note = att.content.optString("note", "");
if (note.length() > 40) {
note = note.substring(0, 40);
}
tvSummary.setText(note);
} else {
StringBuffer status = new StringBuffer(getResources().getString(R.string.status));
if (att.status == Attachment.AVAILABLE)
status.append(getResources().getString(R.string.attachment_zfs_available));
else if (att.status == Attachment.LOCAL)
status.append(getResources().getString(R.string.attachment_zfs_local));
else
status.append(getResources().getString(R.string.attachment_unknown));
tvSummary.setText(att.title + " " + status.toString());
}
return row;
}
});
ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
// Warning here because Eclipse can't tell whether my ArrayAdapter is
// being used with the correct parametrization.
@SuppressWarnings("unchecked")
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// If we have a click on an entry, show its note
ArrayAdapter<Attachment> adapter = (ArrayAdapter<Attachment>) parent.getAdapter();
Attachment row = adapter.getItem(position);
if (row.content.has("note")) {
Log.d(TAG, "Trying to start note view activity for: " + row.key);
Intent i = new Intent(getBaseContext(), NoteActivity.class);
i.putExtra("com.gimranov.zandy.app.attKey", row.key);//row.content.optString("note", ""));
startActivity(i);
}
return true;
}
});
lv.setOnItemClickListener(new OnItemClickListener() {
// Warning here because Eclipse can't tell whether my ArrayAdapter is
// being used with the correct parametrization.
@SuppressWarnings("unchecked")
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// If we have a long click on an entry, do something...
ArrayAdapter<Attachment> adapter = (ArrayAdapter<Attachment>) parent.getAdapter();
Attachment row = adapter.getItem(position);
String url = (row.url != null && !row.url.equals("")) ?
row.url : row.content.optString("url");
if (!row.getType().equals("note")) {
Bundle b = new Bundle();
b.putString("title", row.title);
b.putString("attachmentKey", row.key);
b.putString("content", url);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
int linkMode = row.content.optInt("linkMode", Attachment.MODE_IMPORTED_URL);
if (settings.getBoolean("webdav_enabled", false))
b.putString("mode", "webdav");
else
b.putString("mode", "zfs");
if (linkMode == Attachment.MODE_IMPORTED_FILE
|| linkMode == Attachment.MODE_IMPORTED_URL) {
loadFileAttachment(b);
} else {
AttachmentActivity.this.b = b;
showDialog(DIALOG_CONFIRM_NAVIGATE);
}
}
if (row.getType().equals("note")) {
Bundle b = new Bundle();
b.putString("attachmentKey", row.key);
b.putString("itemKey", itemKey);
b.putString("content", row.content.optString("note", ""));
removeDialog(DIALOG_NOTE);
AttachmentActivity.this.b = b;
showDialog(DIALOG_NOTE);
}
return;
}
});
}
@Override
public void onDestroy() {
if (db != null) db.close();
if (tmpFiles != null) {
for (File f : tmpFiles) {
if (!f.delete()) {
Log.e(TAG, "Failed to delete temporary file on activity close.");
}
}
tmpFiles.clear();
}
super.onDestroy();
}
@Override
public void onResume() {
if (db == null) db = new Database(this);
super.onResume();
}
@Override
protected Dialog onCreateDialog(int id) {
final String attachmentKey = b.getString("attachmentKey");
final String itemKey = b.getString("itemKey");
final String content = b.getString("content");
final String mode = b.getString("mode");
AlertDialog dialog;
switch (id) {
case DIALOG_CONFIRM_NAVIGATE:
dialog = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.view_online_warning))
.setPositiveButton(getResources().getString(R.string.view), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// The behavior for invalid URIs might be nasty, but
// we'll cross that bridge if we come to it.
try {
Uri uri = Uri.parse(content);
startActivity(new Intent(Intent.ACTION_VIEW)
.setData(uri));
} catch (ActivityNotFoundException e) {
// There can be exceptions here; not sure what would prompt us to have
// URIs that the browser can't load, but it apparently happens.
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.attachment_intent_failed_for_uri, content),
Toast.LENGTH_SHORT).show();
}
}
}).setNeutralButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// do nothing
}
}).create();
return dialog;
case DIALOG_CONFIRM_DELETE:
dialog = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.attachment_delete_confirm))
.setPositiveButton(getResources().getString(R.string.menu_delete), new DialogInterface.OnClickListener() {
@SuppressWarnings("unchecked")
public void onClick(DialogInterface dialog, int whichButton) {
Attachment a = Attachment.load(attachmentKey, db);
a.delete(db);
ArrayAdapter<Attachment> la = (ArrayAdapter<Attachment>) getListAdapter();
la.clear();
for (Attachment at : Attachment.forItem(Item.load(itemKey, db), db)) {
la.add(at);
}
}
}).setNegativeButton(getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// do nothing
}
}).create();
return dialog;
case DIALOG_NOTE:
final EditText input = new EditText(this);
input.setText(content, BufferType.EDITABLE);
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.note))
.setView(input)
.setPositiveButton(getResources().getString(R.string.ok), new DialogInterface.OnClickListener() {
@SuppressWarnings("unchecked")
public void onClick(DialogInterface dialog, int whichButton) {
Editable value = input.getText();
String fixed = value.toString().replaceAll("\n\n", "\n<br>");
if (mode != null && mode.equals("new")) {
Log.d(TAG, "Attachment created with parent key: " + itemKey);
Attachment att = new Attachment(getBaseContext(), "note", itemKey);
att.setNoteText(fixed);
att.dirty = APIRequest.API_NEW;
att.save(db);
} else {
Attachment att = Attachment.load(attachmentKey, db);
att.setNoteText(fixed);
att.dirty = APIRequest.API_DIRTY;
att.save(db);
}
ArrayAdapter<Attachment> la = (ArrayAdapter<Attachment>) getListAdapter();
la.clear();
for (Attachment a : Attachment.forItem(Item.load(itemKey, db), db)) {
la.add(a);
}
la.notifyDataSetChanged();
}
}).setNeutralButton(getResources().getString(R.string.cancel),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// do nothing
}
});
// We only want the delete option when this isn't a new note
if (mode == null || !"new".equals(mode)) {
builder = builder.setNegativeButton(getResources().getString(R.string.menu_delete), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Bundle b = new Bundle();
b.putString("attachmentKey", attachmentKey);
b.putString("itemKey", itemKey);
removeDialog(DIALOG_CONFIRM_DELETE);
AttachmentActivity.this.b = b;
showDialog(DIALOG_CONFIRM_DELETE);
}
});
}
dialog = builder.create();
return dialog;
case DIALOG_FILE_PROGRESS:
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setMessage(getResources().getString(R.string.attachment_downloading, b.getString("title")));
mProgressDialog.setIndeterminate(true);
return mProgressDialog;
default:
Log.e(TAG, "Invalid dialog requested");
return null;
}
}
protected void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_FILE_PROGRESS:
mProgressDialog.setMessage(getResources().getString(R.string.attachment_downloading, b.getString("title")));
progressThread = new ProgressThread(handler, b);
progressThread.start();
}
}
private void showAttachment(Attachment att) {
if (att.status == Attachment.LOCAL) {
Log.d(TAG, "Starting to display local attachment");
Uri uri = Uri.fromFile(new File(att.filename));
String mimeType = att.content.optString("mimeType", "application/pdf");
try {
startActivity(new Intent(Intent.ACTION_VIEW)
.setDataAndType(uri, mimeType));
} catch (ActivityNotFoundException e) {
Log.e(TAG, "No activity for intent", e);
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.attachment_intent_failed, mimeType),
Toast.LENGTH_SHORT).show();
}
}
}
/**
* This mainly is to move the logic out of the onClick callback above
* Decides whether to download or view, and launches the appropriate action
*
* @param b
*/
private void loadFileAttachment(Bundle b) {
Attachment att = Attachment.load(b.getString("attachmentKey"), db);
if (!ServerCredentials.sBaseStorageDir.exists())
ServerCredentials.sBaseStorageDir.mkdirs();
if (!ServerCredentials.sDocumentStorageDir.exists())
ServerCredentials.sDocumentStorageDir.mkdirs();
File attFile = new File(att.filename);
if (att.status == Attachment.AVAILABLE
// Zero-length or nonexistent gives length == 0
|| (attFile != null && attFile.length() == 0)) {
Log.d(TAG, "Starting to try and download attachment (status: " + att.status + ", fn: " + att.filename + ")");
this.b = b;
showDialog(DIALOG_FILE_PROGRESS);
} else showAttachment(att);
}
/**
* Refreshes the current list adapter
*/
@SuppressWarnings("unchecked")
private void refreshView() {
ArrayAdapter<Attachment> la = (ArrayAdapter<Attachment>) getListAdapter();
la.clear();
for (Attachment at : Attachment.forItem(item, db)) {
la.add(at);
}
}
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.arg2) {
case ProgressThread.STATE_DONE:
if (mProgressDialog.isShowing())
dismissDialog(DIALOG_FILE_PROGRESS);
refreshView();
if (null != msg.obj)
showAttachment((Attachment) msg.obj);
break;
case ProgressThread.STATE_FAILED:
// Notify that we failed to get anything
Toast.makeText(getApplicationContext(),
getResources().getString(R.string.attachment_no_download_url),
Toast.LENGTH_SHORT).show();
if (mProgressDialog.isShowing())
dismissDialog(DIALOG_FILE_PROGRESS);
// Let's try to fall back on an online version
AttachmentActivity.this.b = msg.getData();
showDialog(DIALOG_CONFIRM_NAVIGATE);
refreshView();
break;
case ProgressThread.STATE_UNZIPPING:
mProgressDialog.setMax(msg.arg1);
mProgressDialog.setProgress(0);
mProgressDialog.setMessage(getResources().getString(R.string.attachment_unzipping));
break;
case ProgressThread.STATE_RUNNING:
mProgressDialog.setMax(msg.arg1);
mProgressDialog.setProgress(0);
mProgressDialog.setIndeterminate(false);
break;
default:
mProgressDialog.setProgress(msg.arg1);
break;
}
}
};
private class ProgressThread extends Thread {
Handler mHandler;
Bundle arguments;
final static int STATE_DONE = 5;
final static int STATE_FAILED = 3;
final static int STATE_RUNNING = 1;
final static int STATE_UNZIPPING = 6;
ProgressThread(Handler h, Bundle b) {
mHandler = h;
arguments = b;
}
@SuppressWarnings("unchecked")
public void run() {
// Setup
final String attachmentKey = arguments.getString("attachmentKey");
// Can't fetch if we have nothing to fetch
if (attachmentKey == null) return;
final String mode = arguments.getString("mode");
URL url;
File file;
String urlstring;
Attachment att = Attachment.load(attachmentKey, db);
String sanitized = att.title.replace(' ', '_');
// If no 1-6-character extension, try to add one using MIME type
if (!sanitized.matches(".*\\.[a-zA-Z0-9]{1,6}$")) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(att.getType());
if (extension != null) sanitized = sanitized + "." + extension;
}
sanitized = sanitized.replaceFirst("^(.*?)(\\.[^.]*)?$", "$1" + "_" + att.key + "$2");
file = new File(ServerCredentials.sDocumentStorageDir, sanitized);
if (!ServerCredentials.sBaseStorageDir.exists())
ServerCredentials.sBaseStorageDir.mkdirs();
if (!ServerCredentials.sDocumentStorageDir.exists())
ServerCredentials.sDocumentStorageDir.mkdirs();
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
if ("webdav".equals(mode)) {
//urlstring = "https://dfs.humnet.ucla.edu/home/ajlyon/zotero/223RMC7C.zip";
//urlstring = "http://www.gimranov.com/research/zotero/223RMC7C.zip";
urlstring = settings.getString("webdav_path", "") + "/" + att.key + ".zip";
Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(settings.getString("webdav_username", ""),
settings.getString("webdav_password", "").toCharArray());
}
});
if (settings.getBoolean("webdav_ssl_override", false)) {
WebDavTrust.installAllTrustingCertificate();
}
} else {
urlstring = att.url + "?key=" + settings.getString("user_key", "");
}
try {
try {
url = new URL(urlstring);
} catch (MalformedURLException e) {
// Alert that we don't have a valid download URL and return
Message msg = mHandler.obtainMessage();
msg.arg2 = STATE_FAILED;
msg.setData(arguments);
mHandler.sendMessage(msg);
Log.e(TAG, "Download URL not valid: " + urlstring, e);
return;
}
//this is the downloader method
long startTime = System.currentTimeMillis();
Log.d(TAG, "download beginning");
Log.d(TAG, "download url:" + url.toString());
Log.d(TAG, "downloaded file name:" + file.getPath());
/* Open a connection to that URL. */
URLConnection ucon = url.openConnection();
ucon.setRequestProperty("User-Agent", "Mozilla/5.0 ( compatible ) ");
ucon.setRequestProperty("Accept", "*/*");
Message msg = mHandler.obtainMessage();
msg.arg1 = ucon.getContentLength();
msg.arg2 = STATE_RUNNING;
mHandler.sendMessage(msg);
/*
* Define InputStreams to read from the URLConnection.
*/
InputStream is = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
final AtomicInteger counter = new AtomicInteger();
OutputStream outputStream = new CountingOutputStream(fos) {
@Override
protected void afterWrite(int n) throws IOException {
super.afterWrite(n);
if (n > 0) {
int completed = counter.addAndGet(n);
Message message = mHandler.obtainMessage();
message.arg1 = completed;
mHandler.sendMessage(message);
}
}
};
is = ucon.getInputStream();
IOUtils.copy(is, outputStream);
} finally {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
}
/* Save to temporary directory for WebDAV */
if ("webdav".equals(mode)) {
if (!ServerCredentials.sCacheDir.exists()) {
//noinspection ResultOfMethodCallIgnored
ServerCredentials.sCacheDir.mkdirs();
}
File tmpFile = File.createTempFile("zandy", ".zip", ServerCredentials.sCacheDir);
FileUtils.copyFile(file, tmpFile);
//noinspection ResultOfMethodCallIgnored
file.delete();
// Keep track of temp files that we've created.
if (tmpFiles == null) tmpFiles = new ArrayList<File>();
tmpFiles.add(tmpFile);
ZipFile zf = new ZipFile(tmpFile);
Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zf.entries();
do {
ZipEntry entry = entries.nextElement();
// Change the message to reflect that we're unzipping now
msg = mHandler.obtainMessage();
msg.arg1 = (int) entry.getSize();
msg.arg2 = STATE_UNZIPPING;
mHandler.sendMessage(msg);
String name64 = entry.getName();
try {
byte[] byteName = Base64.decode(name64.getBytes(), 0, name64.length() - 5, Base64.DEFAULT);
String name = new String(byteName);
Log.d(TAG, "Found file " + name + " from encoded " + name64);
// If the linkMode is not an imported URL (snapshot) and the MIME type isn't text/html,
// then we unzip it and we're happy. If either of the preceding is true, we skip the file
// unless the filename includes .htm (covering .html automatically)
if ((!att.getType().equals("text/html")) || name.contains(".htm")) {
FileOutputStream fos2 = new FileOutputStream(file);
InputStream entryStream = zf.getInputStream(entry);
final AtomicInteger counter = new AtomicInteger();
OutputStream outputStream = new CountingOutputStream(fos2) {
@Override
protected void afterWrite(int n) throws IOException {
super.afterWrite(n);
if (n > 0) {
int completed = counter.addAndGet(n);
Message message = mHandler.obtainMessage();
message.arg1 = completed;
mHandler.sendMessage(message);
}
}
};
IOUtils.copy(entryStream, outputStream);
fos2.close();
entryStream.close();
Log.d(TAG, "Finished reading file");
} else {
Log.d(TAG, "Skipping file: " + name);
}
} catch (IllegalArgumentException e) {
Crashlytics.logException(new Throwable("b64 " + name64, e));
} catch (NegativeArraySizeException e) {
Crashlytics.logException(new Throwable("b64 " + name64, e));
}
} while (entries.hasMoreElements());
zf.close();
// We remove the file from the ArrayList if deletion succeeded;
// otherwise deletion is put off until the activity exits.
if (tmpFile.delete()) {
tmpFiles.remove(tmpFile);
}
}
Log.d(TAG, "download ready in "
+ ((System.currentTimeMillis() - startTime) / 1000)
+ " sec");
} catch (IOException e) {
Log.e(TAG, "Error: ", e);
Crashlytics.logException(e);
toastError(R.string.attachment_download_failed, e.getMessage());
}
att.filename = file.getPath();
File newFile = new File(att.filename);
Message msg = mHandler.obtainMessage();
if (newFile.length() > 0) {
att.status = Attachment.LOCAL;
Log.d(TAG, "File downloaded: " + att.filename);
msg.obj = att;
} else {
Log.d(TAG, "File not downloaded: " + att.filename);
att.status = Attachment.AVAILABLE;
msg.obj = null;
}
att.save(db);
msg.arg2 = STATE_DONE;
mHandler.sendMessage(msg);
}
}
private void toastError(final int resource, final String detail) {
AttachmentActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(AttachmentActivity.this,
AttachmentActivity.this.getString(resource)
+ "\n " + detail,
Toast.LENGTH_LONG
).show();
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.zotero_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Bundle b = new Bundle();
// Handle item selection
switch (item.getItemId()) {
case R.id.do_sync:
if (!ServerCredentials.check(getApplicationContext())) {
Toast.makeText(getApplicationContext(), getResources().getString(R.string.sync_log_in_first),
Toast.LENGTH_SHORT).show();
return true;
}
Log.d(TAG, "Preparing sync requests, starting with present item");
new ZoteroAPITask(getBaseContext()).execute(APIRequest.update(this.item));
Toast.makeText(getApplicationContext(), getResources().getString(R.string.sync_started),
Toast.LENGTH_SHORT).show();
return true;
case R.id.do_new:
b.putString("itemKey", this.item.getKey());
b.putString("mode", "new");
removeDialog(DIALOG_NOTE);
showDialog(DIALOG_NOTE);
return true;
case R.id.do_prefs:
startActivity(new Intent(this, SettingsActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}