package edu.mit.mobile.android.livingpostcards;
/*
* Copyright (C) 2012-2013 MIT Mobile Experience Lab
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation version 2
* of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.app.NavUtils;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;
import com.actionbarsherlock.ActionBarSherlock;
import com.actionbarsherlock.ActionBarSherlock.OnCreateOptionsMenuListener;
import com.actionbarsherlock.ActionBarSherlock.OnOptionsItemSelectedListener;
import com.actionbarsherlock.ActionBarSherlock.OnPrepareOptionsMenuListener;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import edu.mit.mobile.android.flipr.R;
import edu.mit.mobile.android.livingpostcards.DeleteDialogFragment.OnDeleteListener;
import edu.mit.mobile.android.livingpostcards.auth.Authenticator;
import edu.mit.mobile.android.livingpostcards.data.Card;
import edu.mit.mobile.android.livingpostcards.data.CardMedia;
import edu.mit.mobile.android.locast.Constants;
import edu.mit.mobile.android.locast.data.JsonSyncableItem;
import edu.mit.mobile.android.locast.data.PrivatelyAuthorable;
import edu.mit.mobile.android.locast.sync.LocastSyncService;
public class CardEditActivity extends FragmentActivity implements OnCreateOptionsMenuListener,
OnOptionsItemSelectedListener, LoaderCallbacks<Cursor>, OnPrepareOptionsMenuListener,
OnDeleteListener, OnClickListener {
private static final String[] CARD_PROJECTION = new String[] { Card._ID, Card.COL_TITLE,
Card.COL_DESCRIPTION, Card.COL_DRAFT, Card.COL_TIMING, Card.COL_AUTHOR_URI,
Card.COL_PRIVACY };
private static final String TAG = CardEditActivity.class.getSimpleName();
public static final String PREF_DIALOG_COLLABORATIVE_SEEN = "CardEditActivity.DIALOG_COLLABORATIVE_SEEN";
private static final String TAG_DELETE_DIALOG = "delete-dialog";
private static final String TAG_DIALOG_COLLABORATIVE = "dialog-collaborative";
private Uri mCard;
private CardMediaEditFragment mCardViewFragment;
private final ActionBarSherlock mSherlock = ActionBarSherlock.wrap(this);
private String mUserUri;
private boolean mIsEditable;
private boolean mIsDraft;
private EditText mTitle;
private EditText mDescription;
private final SetCollabDescDialogFragment.OnMarkCollaborativeListener mOnMarkCollabListener = new SetCollabDescDialogFragment.OnMarkCollaborativeListener() {
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
@Override
public void onMarkCollaborative() {
final SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(CardEditActivity.this);
final boolean neverShowAgain = false;
if (neverShowAgain) {
final Editor e = prefs.edit().putBoolean(PREF_DIALOG_COLLABORATIVE_SEEN, true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
e.apply(); // this was introduced in Gingerbread
} else {
e.commit();
}
}
setCollaborative(true);
}
};
private boolean mIsOwner;
private boolean mIsCollaborative;
private boolean mSaveOnPause = false;
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
mSherlock.setContentView(R.layout.activity_card_edit);
final ActionBar ab = mSherlock.getActionBar();
ab.setDisplayHomeAsUpEnabled(true);
mTitle = (EditText) findViewById(R.id.title);
mDescription = (EditText) findViewById(R.id.description);
final View addFrame = findViewById(R.id.add_frame);
addFrame.setOnClickListener(this);
mCard = getIntent().getData();
final String action = getIntent().getAction();
final FragmentManager fm = getSupportFragmentManager();
if (Intent.ACTION_EDIT.equals(action) || Intent.ACTION_DELETE.equals(action)) {
final FragmentTransaction ft = fm.beginTransaction();
final Fragment f = fm.findFragmentById(R.id.card_edit_fragment);
if (f != null) {
mCardViewFragment = (CardMediaEditFragment) f;
} else {
mCardViewFragment = CardMediaEditFragment.newInstance(Card.MEDIA.getUri(mCard));
ft.replace(R.id.card_edit_fragment, mCardViewFragment);
}
// if the dialog has been automatically restored by the system, hook it in.
final SetCollabDescDialogFragment collab = (SetCollabDescDialogFragment) fm
.findFragmentByTag(TAG_DIALOG_COLLABORATIVE);
if (collab != null) {
collab.setOnMarkCollaborativeListener(mOnMarkCollabListener);
}
mUserUri = Authenticator.getUserUri(this, Authenticator.ACCOUNT_TYPE);
getSupportLoaderManager().initLoader(0, null, this);
// if this isn't null, it was saved automatically for us. So hook it back in.
final DeleteDialogFragment deleteDialog = (DeleteDialogFragment) fm
.findFragmentByTag(TAG_DELETE_DIALOG);
if (deleteDialog != null) {
deleteDialog.registerOnDeleteListener(this);
} else if (Intent.ACTION_DELETE.equals(action)) {
onDeletePostcard();
}
ft.commit();
}
}
private void onDeletePostcard() {
final DeleteDialogFragment del = DeleteDialogFragment.newInstance(mCard,
getText(R.string.delete_postcard),
getText(R.string.postcard_edit_delete_confirm_message));
del.registerOnDeleteListener(this);
del.show(getSupportFragmentManager(), TAG_DELETE_DIALOG);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.publish:
onPublish();
return true;
case R.id.save:
onSave();
return true;
case R.id.delete:
onDeletePostcard();
return true;
case android.R.id.home: {
final Intent i = new Intent(Intent.ACTION_VIEW, mCard);
NavUtils.navigateUpTo(this, i);
}
return true;
case R.id.add_frame:
// this is conceptually starting it for a result, but the result isn't actually
// used.
startActivityForResult(new Intent(CameraActivity.ACTION_ADD_PHOTO, mCard), 0);
return true;
case R.id.make_collaborative:
onMakeCollaborative();
return true;
case R.id.make_personal:
onMakePersonal();
return true;
default:
return false;
}
}
/**
* Queries the card media to determine if the card media have any contributors other than the
* owner of the card.
*
* @param card
* @return
*/
private boolean hasNonOwnerContributors(Uri card) {
boolean hasNonOwnerContributors = false;
final String myAuthorUri = Authenticator.getUserUri(this);
final String[] proj = new String[] { CardMedia.COL_AUTHOR_URI };
final Cursor media = Card.MEDIA.query(getContentResolver(), card, proj);
final int cardMediaAuthorCol = media.getColumnIndexOrThrow(CardMedia.COL_AUTHOR_URI);
try {
for (media.moveToFirst(); !hasNonOwnerContributors && !media.isAfterLast(); media
.moveToNext()) {
final String cardAuthor = media.getString(cardMediaAuthorCol);
hasNonOwnerContributors |= !myAuthorUri.equals(cardAuthor);
}
} finally {
media.close();
}
return hasNonOwnerContributors;
}
public static class SetCollabDescDialogFragment extends DialogFragment implements
DialogInterface.OnClickListener {
public interface OnMarkCollaborativeListener {
public void onMarkCollaborative();
}
private OnMarkCollaborativeListener mListener;
public void setOnMarkCollaborativeListener(OnMarkCollaborativeListener l) {
mListener = l;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity()).setIcon(R.drawable.ic_launcher)
.setTitle(R.string.make_collaborative)
.setPositiveButton(R.string.make_collaborative, this)
.setMessage(R.string.make_collaborative_feature_description)
.setNegativeButton(android.R.string.cancel, this).setCancelable(true).create();
}
@Override
public void onDestroy() {
super.onDestroy();
mListener = null;
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
mListener.onMarkCollaborative();
break;
case DialogInterface.BUTTON_NEGATIVE:
dismiss();
break;
}
}
}
private void setCollaborative(boolean collaborative) {
final boolean success = Card.setCollaborative(getContentResolver(), mCard, collaborative);
if (success) {
if (Constants.DEBUG) {
Log.d(TAG, mCard + " saved successfully.");
}
LocastSyncService.startSync(this, mCard, true);
}
}
private void onMakeCollaborative() {
if (mIsCollaborative) {
return;
}
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final boolean dialogSeen = prefs.getBoolean(PREF_DIALOG_COLLABORATIVE_SEEN, false);
if (!dialogSeen) {
final SetCollabDescDialogFragment df = new SetCollabDescDialogFragment();
df.setOnMarkCollaborativeListener(mOnMarkCollabListener);
df.show(getSupportFragmentManager(), TAG_DIALOG_COLLABORATIVE);
} else {
setCollaborative(true);
Toast.makeText(this, R.string.notice_make_collaborative_success, Toast.LENGTH_SHORT)
.show();
}
}
private void onMakePersonal() {
if (!mIsCollaborative) {
return;
}
new AsyncTask<Uri, Void, Boolean>() {
@Override
protected Boolean doInBackground(Uri... params) {
boolean madePersonal = false;
if (!hasNonOwnerContributors(mCard)) {
madePersonal = Card.setCollaborative(getContentResolver(), mCard, false);
}
return madePersonal;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
Toast.makeText(CardEditActivity.this,
R.string.notice_make_card_personal_success, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(CardEditActivity.this,
R.string.notice_cannot_make_card_personal, Toast.LENGTH_LONG).show();
}
}
}.execute(mCard);
}
private boolean validate() {
if (mTitle.length() == 0) {
mTitle.setError(getString(R.string.postcard_edit_title_empty));
mTitle.requestFocus();
return false;
}
return true;
}
@Override
protected void onPause() {
super.onPause();
if (mSaveOnPause) {
save();
}
}
/**
* Saves the card without any validation.
*
* @return true if save was successful.
*/
private boolean save() {
final ContentValues cv = new ContentValues();
cv.put(Card.COL_DRAFT, mIsDraft);
cv.put(Card.COL_TITLE, mTitle.getText().toString());
cv.put(Card.COL_DESCRIPTION, mDescription.getText().toString());
cv.put(Card.COL_TIMING, mCardViewFragment.getAnimationTiming());
final int updated = getContentResolver().update(mCard, cv, null, null);
final boolean success = updated == 1;
if (success) {
if (Constants.DEBUG) {
Log.d(TAG, mCard + " saved successfully.");
}
LocastSyncService.startSync(this, mCard, true);
}
return success;
}
private void onSave() {
if (!validate()) {
return;
}
if (save()) {
finish();
} else {
Toast.makeText(this, R.string.err_publish_fail, Toast.LENGTH_LONG).show();
}
}
protected void onPublish() {
if (!validate()) {
return;
}
mIsDraft = false;
if (save()) {
LocastSyncService.startSync(this, mCard, true);
finish();
Toast.makeText(this, R.string.notice_publish_success, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, R.string.err_publish_fail, Toast.LENGTH_LONG).show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mSherlock.getMenuInflater().inflate(R.menu.activity_card_edit, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(android.view.Menu menu) {
return mSherlock.dispatchPrepareOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// only the owner can do the following.
menu.findItem(R.id.delete).setVisible(mIsOwner);
menu.findItem(R.id.delete_photos).setVisible(mIsOwner);
menu.findItem(R.id.publish).setVisible(mIsOwner && mIsDraft);
menu.findItem(R.id.save).setVisible(mIsOwner && !mIsDraft);
final MenuItem makeCollab = menu.findItem(R.id.make_collaborative);
makeCollab.setVisible(mIsOwner && !mIsCollaborative);
final MenuItem makePersonal = menu.findItem(R.id.make_personal);
makePersonal.setVisible(mIsOwner && mIsCollaborative);
return true;
}
@Override
public boolean onCreateOptionsMenu(android.view.Menu menu) {
return mSherlock.dispatchCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
return mSherlock.dispatchOptionsItemSelected(item);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(this, mCard, CARD_PROJECTION, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
if (c.moveToFirst()) {
mTitle.setText(c.getString(c.getColumnIndex(Card.COL_TITLE)));
mDescription.setText(c.getString(c.getColumnIndex(Card.COL_DESCRIPTION)));
mIsEditable = PrivatelyAuthorable.canEdit(mUserUri, c);
findViewById(R.id.add_frame).setVisibility(mIsEditable ? View.VISIBLE : View.GONE);
mIsOwner = mUserUri.equals(c.getString(c.getColumnIndexOrThrow(Card.COL_AUTHOR_URI)));
final String privacy = c.getString(c.getColumnIndexOrThrow(Card.COL_PRIVACY));
mIsCollaborative = Card.PRIVACY_PUBLIC.equals(privacy);
mTitle.setEnabled(mIsEditable && mIsOwner);
mDescription.setEnabled(mIsEditable && mIsOwner);
mIsDraft = JsonSyncableItem.isDraft(c);
mSherlock.dispatchInvalidateOptionsMenu();
final int timing = c.getInt(c.getColumnIndexOrThrow(Card.COL_TIMING));
mCardViewFragment.setAnimationTiming(timing);
mSaveOnPause = true;
} else {
mSaveOnPause = false;
finish();
}
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
mSaveOnPause = false;
}
@Override
public void onDelete(Uri item, boolean deleted) {
if (mCard.equals(item) && deleted) {
mSaveOnPause = false;
setResult(RESULT_OK);
// no need to call finish, as the loader will automatically reload, which will result in
// no data being loaded, which will then call finish()
} else if (Intent.ACTION_DELETE.equals(getIntent().getAction()) && !deleted) {
setResult(RESULT_CANCELED);
finish();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.add_frame:
// this is conceptually starting it for a result, but the result isn't actually
// used.
startActivityForResult(new Intent(CameraActivity.ACTION_ADD_PHOTO, mCard), 0);
break;
default:
break;
}
}
}