Java tutorial
/**************************************************************************************** * Copyright (c) 2013 Bibek Shrestha <bibekshrestha@gmail.com> * * Copyright (c) 2013 Zaur Molotnikov <qutorial@gmail.com> * * Copyright (c) 2013 Nicolas Raoul <nicolas.raoul@gmail.com> * * Copyright (c) 2013 Flavio Lerda <flerda@gmail.com> * * * * 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; either version 3 of the License, or (at your option) any later * * version. * * * * 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/>. * ****************************************************************************************/ package com.hichinaschool.flashcards.anki.multimediacard.activity; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; import com.hichinaschool.flashcards.anim.ActivityTransitionAnimation; import com.hichinaschool.flashcards.anki.AnkiDroidApp; import com.hichinaschool.flashcards.anki.R; import com.hichinaschool.flashcards.anki.multimediacard.AudioView; import com.hichinaschool.flashcards.anki.multimediacard.IMultimediaEditableNote; import com.hichinaschool.flashcards.anki.multimediacard.fields.IField; import com.hichinaschool.flashcards.anki.multimediacard.impl.MultimediaEditableNote; import com.hichinaschool.flashcards.anki.servicelayer.NoteService; import com.hichinaschool.flashcards.async.DeckTask; import com.hichinaschool.flashcards.async.DeckTask.TaskData; import com.hichinaschool.flashcards.async.DeckTask.TaskListener; import com.hichinaschool.flashcards.libanki.Card; import com.hichinaschool.flashcards.libanki.Collection; import com.hichinaschool.flashcards.libanki.Note; import com.hichinaschool.flashcards.themes.StyledDialog; import com.hichinaschool.flashcards.utils.BitmapUtil; import com.hichinaschool.flashcards.utils.ExifUtil; import com.hichinaschool.flashcards.utils.JSONNameComparator; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.util.ArrayList; import java.util.Collections; public class MultimediaCardEditorActivity extends Activity { private static final String BUNDLE_KEY_SHUT_OFF = "key.multimedia.shut.off"; public static final String EXTRA_CALLER = "CALLER"; public static final int CALLER_NOCALLER = 0; public static final int CALLER_INDICLASH = 10; public static final int REQUEST_CODE_EDIT_FIELD = 1; public static final int RESULT_DELETED = 2; // -1, 0, 1 are taken by default // RESULT_CANCELLED, FIRST_USER, // DELETED public static final String EXTRA_FIELD_INDEX = "multim.card.ed.extra.field.index"; public static final String EXTRA_FIELD = "multim.card.ed.extra.field"; public static final String EXTRA_WHOLE_NOTE = "multim.card.ed.extra.whole.note"; public static final String EXTRA_CARD_ID = "CARD_ID"; // private static final String ACTION_CREATE_FLASHCARD = // "org.openintents.action.CREATE_FLASHCARD"; // private static final String ACTION_CREATE_FLASHCARD_SEND = "android.intent.action.SEND"; private static final int DIALOG_MODEL_SELECT = 1; private static final int DIALOG_DECK_SELECT = 2; private LinearLayout mEditorLayout; private Button mModelButton; private Button mDeckButton; /* Data variables below, not UI Elements */ private Collection mCol; private long mCurrentDid; private IMultimediaEditableNote mNote; private Note mEditorNote; private Card mCard; /** * Indicates if editor is working with new note or one that exists already in the collection */ private boolean mAddNote; private DisplayMetrics mMetrics; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { boolean b = savedInstanceState.getBoolean(BUNDLE_KEY_SHUT_OFF, false); if (b) { finishCancel(); return; } } setContentView(R.layout.activity_multimedia_card_editor); initData(); initUI(); createEditorUI(mNote); } /** * Creates a TabBar in case action bar is not present as well as other UI Elements */ private void initUI() { int currentapiVersion = android.os.Build.VERSION.SDK_INT; if (currentapiVersion <= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { LinearLayout linearLayout = (LinearLayout) findViewById(R.id.LinearLayoutForSpareMenu); createSpareMenu(linearLayout); } mEditorLayout = (LinearLayout) findViewById(R.id.LinearLayoutInScrollView); LinearLayout mToolsLayout = (LinearLayout) findViewById(R.id.LinearLayoutForButtons); LayoutParams pars = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 1); Button swapButton = new Button(this); swapButton.setText(gtxt(R.string.multimedia_editor_activity_swap_button)); mToolsLayout.addView(swapButton, pars); swapButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { swap(); } }); LinearLayout mButtonsLayout = new LinearLayout(this); mButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); mToolsLayout.addView(mButtonsLayout, pars); pars = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1); // DEPRECATED< BUT USED IN THE PROJECT. mModelButton = new Button(this); mModelButton.setOnClickListener(new OnClickListener() { @SuppressWarnings("deprecation") @Override public void onClick(View v) { showDialog(DIALOG_MODEL_SELECT); } }); mButtonsLayout.addView(mModelButton, pars); mDeckButton = new Button(this); mDeckButton.setOnClickListener(new OnClickListener() { @SuppressWarnings("deprecation") @Override public void onClick(View v) { showDialog(DIALOG_DECK_SELECT); } }); mButtonsLayout.addView(mDeckButton, pars); } protected void swap() { mNote.circularSwap(); createEditorUI(mNote); } private void createSpareMenu(LinearLayout linearLayout) { LayoutParams pars = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1); Button saveButton = new Button(this); saveButton.setText(getString(R.string.CardEditorSaveButton)); saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { save(); } }); linearLayout.addView(saveButton, pars); Button deleteButton = new Button(this); deleteButton.setText(getString(R.string.menu_delete_note)); deleteButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { delete(); } }); linearLayout.addView(deleteButton, pars); } /** * Creates the UI for editor area inside EditorLayout * * @param note */ private void createEditorUI(IMultimediaEditableNote note) { try { if (mNote == null) { finishCancel(); return; } else { for (int i = 0; i < mNote.getNumberOfFields(); ++i) { IField f = mNote.getField(i); if (f == null) { finishCancel(); return; } } } LinearLayout linearLayout = mEditorLayout; linearLayout.removeAllViews(); for (int i = 0; i < note.getNumberOfFields(); ++i) { createNewViewer(linearLayout, note.getField(i), i); } mModelButton.setText(gtxt(R.string.multimedia_editor_activity_note_type) + " : " + mEditorNote.model().getString("name")); mDeckButton.setText(gtxt(R.string.multimedia_editor_activity_deck) + " : " + mCol.getDecks().get(mCurrentDid).getString("name")); } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); return; } } private String gtxt(int id) { return getText(id).toString(); } private void putExtrasAndStartEditActivity(final IField field, final int index, Intent i) { i.putExtra(EXTRA_FIELD_INDEX, index); i.putExtra(EXTRA_FIELD, field); i.putExtra(EXTRA_WHOLE_NOTE, mNote); startActivityForResult(i, REQUEST_CODE_EDIT_FIELD); } private void createNewViewer(LinearLayout linearLayout, final IField field, final int index) { final MultimediaCardEditorActivity context = this; switch (field.getType()) { case TEXT: // Create a text field and an edit button, opening editing for // the // text field TextView textView = new TextView(this); textView.setText(field.getText()); linearLayout.addView(textView, LinearLayout.LayoutParams.MATCH_PARENT); break; case IMAGE: ImageView imgView = new ImageView(this); // // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inSampleSize = 2; // Bitmap bm = BitmapFactory.decodeFile(myJpgPath, options); // jpgView.setImageBitmap(bm); LinearLayout.LayoutParams p = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); File f = new File(field.getImagePath()); Bitmap b = BitmapUtil.decodeFile(f, getMaxImageSize()); int currentapiVersion = android.os.Build.VERSION.SDK_INT; if (currentapiVersion >= android.os.Build.VERSION_CODES.ECLAIR) { b = ExifUtil.rotateFromCamera(f, b); } imgView.setImageBitmap(b); imgView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); imgView.setAdjustViewBounds(true); DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); int height = metrics.heightPixels; int width = metrics.widthPixels; imgView.setMaxHeight((int) Math.round(height * 0.6)); imgView.setMaxWidth((int) Math.round(width * 0.7)); linearLayout.addView(imgView, p); break; case AUDIO: AudioView audioView = AudioView.createPlayerInstance(this, R.drawable.av_play, R.drawable.av_pause, R.drawable.av_stop, field.getAudioPath()); linearLayout.addView(audioView); break; default: Log.e("multimedia editor", "Unsupported field type found"); break; } Button editButtonText = new Button(this); editButtonText.setText(gtxt(R.string.multimedia_editor_activity_edit_button)); linearLayout.addView(editButtonText, LinearLayout.LayoutParams.MATCH_PARENT); editButtonText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent i = new Intent(context, EditFieldActivity.class); putExtrasAndStartEditActivity(field, index, i); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_multimedia_card_editor, menu); return true; } // Temporary implemented // private IMultimediaEditableNote getMockNote() // { // return MockNoteFactory.makeNote(); // } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.multimedia_delete_note: delete(); return true; case R.id.multimedia_save_note: save(); return true; case android.R.id.home: return true; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_EDIT_FIELD) { if (resultCode == RESULT_OK) { IField field = (IField) data.getSerializableExtra(EditFieldActivity.EXTRA_RESULT_FIELD); int index = data.getIntExtra(EditFieldActivity.EXTRA_RESULT_FIELD_INDEX, -1); // Failed editing activity if (index == -1) { return; } mNote.setField(index, field); createEditorUI(mNote); } super.onActivityResult(requestCode, resultCode, data); } } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_MODEL_SELECT: return showModelSelectDialog(); case DIALOG_DECK_SELECT: return showDeckSelectDialog(); default: break; } return null; } private Dialog showModelSelectDialog() { StyledDialog dialog = null; StyledDialog.Builder builder = new StyledDialog.Builder(this); ArrayList<CharSequence> dialogItems = new ArrayList<CharSequence>(); // Use this array to know which ID is associated with each // Item(name) final ArrayList<Long> dialogIds = new ArrayList<Long>(); ArrayList<JSONObject> models = mCol.getModels().all(); Collections.sort(models, new JSONNameComparator()); builder.setTitle(R.string.note_type); for (JSONObject m : models) { try { dialogItems.add(m.getString("name")); dialogIds.add(m.getLong("id")); } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); } } // Convert to Array String[] items2 = new String[dialogItems.size()]; dialogItems.toArray(items2); builder.setItems(items2, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { long oldModelId; try { oldModelId = mCol.getModels().current().getLong("id"); } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); return; } long newId = dialogIds.get(item); if (oldModelId != newId) { changeCurrentModel(newId); createEditorUI(mNote); } } }); dialog = builder.create(); return dialog; } private Dialog showDeckSelectDialog() { StyledDialog dialog = null; StyledDialog.Builder builder = new StyledDialog.Builder(this); ArrayList<CharSequence> dialogDeckItems = new ArrayList<CharSequence>(); // Use this array to know which ID is associated with each // Item(name) final ArrayList<Long> dialogDeckIds = new ArrayList<Long>(); ArrayList<JSONObject> decks = mCol.getDecks().all(); Collections.sort(decks, new JSONNameComparator()); builder.setTitle(R.string.deck); for (JSONObject d : decks) { try { if (d.getInt("dyn") == 0) { dialogDeckItems.add(d.getString("name")); dialogDeckIds.add(d.getLong("id")); } } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); } } // Convert to Array String[] items = new String[dialogDeckItems.size()]; dialogDeckItems.toArray(items); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { long newId = dialogDeckIds.get(item); if (mCurrentDid != newId) { if (mAddNote) { try { // TODO: mEditorNote.setDid(newId); mEditorNote.model().put("did", newId); mCol.getModels().setChanged(); } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); } } mCurrentDid = newId; createEditorUI(mNote); } } }); dialog = builder.create(); return dialog; } /** * Change current model for the Note. Changes both MultimediaNote and the mEditorNote (Note Object) and copies * existing values to both. * * @param newId */ protected void changeCurrentModel(long newId) { try { JSONObject currentModel = mCol.getModels().get(newId); mCol.getModels().setCurrent(currentModel); JSONObject cdeck = mCol.getDecks().current(); cdeck.put("mid", newId); mCol.getDecks().save(cdeck); int size = mNote.getNumberOfFields(); String[] oldValues = new String[size]; for (int i = 0; i < size; i++) { oldValues[i] = mNote.getField(i).getFormattedValue(); } mEditorNote = new Note(mCol, currentModel); mEditorNote.model().put("did", mCurrentDid); MultimediaEditableNote newNote = NoteService.createEmptyNote(currentModel); for (int i = 0; i < newNote.getNumberOfFields(); i++) { if (i < mNote.getNumberOfFields()) { newNote.setField(i, mNote.getField(i)); } } mNote = newNote; } catch (JSONException e) { Log.e("Multimedia Editor", e.getMessage()); } } private void delete() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: if (mAddNote) { finish(); } else { // Do not really delete the node here, instead let // the caller deal with deletion. // FIXME: Since only CardBrowser calls us, // CardBrowser handles deletion and Listview Update setResult(RESULT_DELETED); finish(); } break; case DialogInterface.BUTTON_NEGATIVE: // Do nothing break; } } }; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(gtxt(R.string.multimedia_editor_activity_delete_question)) .setPositiveButton(gtxt(R.string.multimedia_editor_activity_delete_confirm), dialogClickListener) .setNegativeButton(gtxt(R.string.multimedia_editor_activity_delete_reject), dialogClickListener) .show(); } /** * If a note has been passed, loads it. Otherwise creates a new Note It checks for a noteId, a long value with * EXTRA_NOTE_ID as key. If not 0, edits the note, else creates a new note */ private void initData() { try { mCol = AnkiDroidApp.getCol(); Intent callerIntent = getIntent(); long cardId = callerIntent.getLongExtra(EXTRA_CARD_ID, 0); if (cardId != 0) { mCard = mCol.getCard(cardId); mCurrentDid = mCard.getDid(); mEditorNote = mCard.note(); mCol.getModels().setCurrent(mEditorNote.model()); mNote = NoteService.createEmptyNote(mEditorNote.model()); NoteService.updateMultimediaNoteFromJsonNote(mEditorNote, mNote); mAddNote = false; } else { mCurrentDid = mCol.getDecks().current().getLong("id"); if (mCol.getDecks().isDyn(mCurrentDid)) { mCurrentDid = 1; } mAddNote = true; JSONObject currentModel = mCol.getModels().current(); mNote = NoteService.createEmptyNote(currentModel); if (mNote == null) { Log.e("Multimedia Editor", "Cannot create a Note"); throw new Exception("Could not create a not"); } mEditorNote = new Note(mCol, currentModel); mEditorNote.model().put("did", mCurrentDid); } // TODO take care of tags @see CardEditor setNote(Note) } catch (Exception e) { Log.e("Multimedia Editor", e.getMessage()); finishCancel(); return; } } private void finishCancel() { Intent resultData = new Intent(); setResult(RESULT_CANCELED, resultData); finish(); } private void save() { NoteService.saveMedia((MultimediaEditableNote) mNote); NoteService.updateJsonNoteFromMultimediaNote(mNote, mEditorNote); TaskListener listener = new TaskListener() { @Override public void onProgressUpdate(TaskData... values) { // TODO Auto-generated method stub } @Override public void onPreExecute() { // TODO Auto-generated method stub } @Override public void onPostExecute(TaskData result) { // TODO Auto-generated method stub } }; if (mAddNote) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_ADD_FACT, listener, new DeckTask.TaskData(mEditorNote)); } else { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_UPDATE_FACT, listener, new DeckTask.TaskData(mCol.getSched(), mCard, false)); } setResult(Activity.RESULT_OK); finish(); animateRight(); } private void animateRight() { if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.FADE); } } private int getMaxImageSize() { DisplayMetrics metrics = getDisplayMetrics(); int height = metrics.heightPixels; int width = metrics.widthPixels; return (int) Math.min(height * 0.4, width * 0.6); } protected DisplayMetrics getDisplayMetrics() { if (mMetrics == null) { mMetrics = new DisplayMetrics(); this.getWindowManager().getDefaultDisplay().getMetrics(mMetrics); } return mMetrics; } @Override protected void onDestroy() { super.onDestroy(); if (mEditorLayout == null) { return; } int n = mEditorLayout.getChildCount(); for (int i = 0; i < n; ++i) { View child = mEditorLayout.getChildAt(i); if (child instanceof ImageView) { ImageView iv = (ImageView) child; BitmapUtil.freeImageView(iv); } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(BUNDLE_KEY_SHUT_OFF, true); } }