package com.cwport.sentencer; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.media.AudioManager; import android.media.MediaPlayer; import android.support.v4.app.NavUtils; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.NumberPicker; import android.widget.TextView; import android.widget.Toast; import com.cwport.sentencer.data.DataException; import com.cwport.sentencer.data.DataHelper; import com.cwport.sentencer.data.DataManager; import com.cwport.sentencer.data.SourceType; import com.cwport.sentencer.model.Card; import com.cwport.sentencer.model.Lesson; import com.cwport.sentencer.media.TextToSpeech; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Random; import java.util.Set; public class CardActivity extends ActionBarActivity { private final String TAG = CardActivity.class.getSimpleName(); private String lessonTitle; private String lessonId; private SourceType lessonSource; private int cardIndex = 0; private int cardCount = 0; private boolean flip = false; private ArrayList<Card> cards = new ArrayList<Card>(); private Lesson lesson; private TextView textView; private Button btnNext; private Button btnFlip; private Button btnPrev; private Button btnPlay; private boolean showMarked = false; private boolean shuffled = false; private boolean showBackFirst = false; private boolean forceRewind = false; private String textLocale; // text locale of the current card side private Set<String> markedCardsIdSet = new HashSet<String>(); private ArrayList<String> ttsUrls = new ArrayList<String>(); private int ttsCounter = 0; private DataManager dataManager; MediaPlayer mediaPlayer = new MediaPlayer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_card); getSupportActionBar().setDisplayHomeAsUpEnabled(true); dataManager = ((SentencerApp)getApplicationContext()).getDataManager(); // get parameters from intent Intent i = getIntent(); this.lessonId = i.getStringExtra(DataHelper.EXTRA_LESSON_INDEX); this.lessonTitle = i.getStringExtra(DataHelper.EXTRA_LESSON_TITLE); this.lessonSource = SourceType.values()[i.getIntExtra(DataHelper.EXTRA_LESSON_SOURCE, 0)]; this.textView = (TextView) findViewById(R.id.card_text); this.textView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { markCard(); return false; } }); this.btnPlay = (Button) findViewById(R.id.button_play); this.btnPlay.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if(CommonUtils.isConnectedOrConnectingToNetwork(getApplicationContext())) { textToSpeech(v, textLocale, textView.getText().toString()); } else { Toast.makeText(getApplicationContext(), R.string.msg_no_internet_connection, Toast.LENGTH_LONG).show(); } } } ); this.btnPrev = (Button) findViewById(R.id.button_prev); this.btnFlip = (Button) findViewById(R.id.button_flip); this.btnNext = (Button) findViewById(R.id.button_next); try { if(savedInstanceState != null) { this.lessonId = savedInstanceState.getString(DataHelper.PARAM_LESSON_ID); this.lessonSource = SourceType.values()[savedInstanceState.getInt(DataHelper.PARAM_LESSON_SOURCE)]; this.cardIndex = savedInstanceState.getInt(DataHelper.PARAM_CARD_INDEX); this.flip = savedInstanceState.getBoolean(DataHelper.PARAM_FLIP); this.shuffled = savedInstanceState.getBoolean(DataHelper.PARAM_SHUFFLED); this.showMarked = savedInstanceState.getBoolean(DataHelper.PARAM_SHOWMARKED); this.showBackFirst = savedInstanceState.getBoolean(DataHelper.PARAM_SHOWBACKFIRST); String markedCardIdArray[] = savedInstanceState.getStringArray(DataHelper.PARAM_MARKED_CARDS); this.markedCardsIdSet = new HashSet<String>(); Collections.addAll(this.markedCardsIdSet, markedCardIdArray); } this.lesson = dataManager.getDataProvider(this.lessonSource, getApplicationContext()).getLesson(this.lessonId); SharedPreferences sharedPref = this.getSharedPreferences( DataHelper.PREF_PREFIX + this.lesson.getFilename(), Context.MODE_PRIVATE); this.cardIndex = sharedPref.getInt(DataHelper.PARAM_CARD_INDEX, 0); this.shuffled = sharedPref.getBoolean(DataHelper.PARAM_SHUFFLED, false); this.showMarked = sharedPref.getBoolean(DataHelper.PARAM_SHOWMARKED, false); this.showBackFirst = sharedPref.getBoolean(DataHelper.PARAM_SHOWBACKFIRST, false); this.markedCardsIdSet = sharedPref.getStringSet(DataHelper.PARAM_MARKED_CARDS, new HashSet<String>()); this.flip = this.showBackFirst; initCards(); showCard(); } catch(DataException de) { Log.e(TAG, de.getMessage()); this.textView.setText(de.getMessage()); } catch(Exception ex) { Log.e(TAG, ex.getMessage()); this.textView.setText(ex.getMessage()); } } @Override protected void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putString(DataHelper.PARAM_LESSON_ID, this.lessonId); bundle.putInt(DataHelper.PARAM_LESSON_SOURCE, this.lessonSource.ordinal()); bundle.putString(DataHelper.PARAM_LESSON, lessonTitle); bundle.putInt(DataHelper.PARAM_CARD_INDEX, this.cardIndex); bundle.putBoolean(DataHelper.PARAM_FLIP, this.flip); bundle.putBoolean(DataHelper.PARAM_SHUFFLED, this.shuffled); bundle.putBoolean(DataHelper.PARAM_SHOWMARKED, this.showMarked); bundle.putBoolean(DataHelper.PARAM_SHOWBACKFIRST, this.showBackFirst); bundle.putStringArray(DataHelper.PARAM_MARKED_CARDS, getMarkedCardIdArray()); } @Override protected void onStop() { super.onStop(); // Save SharedPreferences on activity stop SharedPreferences sharedPref = this.getSharedPreferences( DataHelper.PREF_PREFIX + this.lesson.getFilename(), Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(DataHelper.PARAM_CARD_INDEX, this.cardIndex); editor.putBoolean(DataHelper.PARAM_SHUFFLED, this.shuffled); editor.putBoolean(DataHelper.PARAM_SHOWMARKED, this.showMarked); editor.putBoolean(DataHelper.PARAM_SHOWBACKFIRST, this.showBackFirst); editor.putStringSet(DataHelper.PARAM_MARKED_CARDS, getMarkedCardsIdSet()); editor.commit(); } private Set<String> getMarkedCardsIdSet() { Set<String> markedSet = new HashSet<String>(); for(Card c: this.cards) { if(c.isMarked()) { markedSet.add(c.getId()); } } return markedSet; } private String[] getMarkedCardIdArray() { Set<String> markedSet = getMarkedCardsIdSet(); String markedIdArray[] = new String[markedSet.size()]; markedIdArray = markedSet.toArray(markedIdArray); return markedIdArray; } private void initCards() throws DataException { this.cards = new ArrayList<Card>(lesson.getCards()); for (Card c : this.cards) { c.setMarked(this.markedCardsIdSet.contains(c.getId())); } if(this.showMarked) { Iterator<Card> iterator = this.cards.iterator(); while(iterator.hasNext()) { Card c = iterator.next(); if(!c.isMarked()) { iterator.remove(); } } } if(this.cards.size() > 0 && this.shuffled) { Collections.shuffle(this.cards, new Random()); this.cardIndex = 0; } this.cardCount = cards.size(); if(this.cardIndex > (this.cardCount - 1) || this.forceRewind) this.cardIndex = 0; // to avoid exception this.forceRewind = false; } /** * Count marked cards in current lesson * @return Number of marked cards */ public int countMarked() { int count = 0; Iterator<Card> iterator = this.cards.iterator(); while(iterator.hasNext()) { Card c = iterator.next(); if(c.isMarked()) { count++; } } return count; } private void initNavigation() { if (this.cardCount > 0) { this.btnPrev.setVisibility(this.cardIndex > 0 ? View.VISIBLE : View.INVISIBLE); this.btnFlip.setVisibility(this.cardCount > 0 ? View.VISIBLE : View.INVISIBLE); this.btnNext.setVisibility((this.cardIndex + 1) < this.cardCount ? View.VISIBLE : View.INVISIBLE); this.setTitle(this.lessonTitle + " (" + (this.cardIndex + 1) + "/" + this.cardCount + ")"); if(TextToSpeech.localeSupported(this.textLocale)) { this.btnPlay.setVisibility(View.VISIBLE); } else { this.btnPlay.setVisibility(View.INVISIBLE); } } else { this.btnPrev.setVisibility(View.INVISIBLE); this.btnFlip.setVisibility(View.INVISIBLE); this.btnNext.setVisibility(View.INVISIBLE); this.btnPlay.setVisibility(View.INVISIBLE); this.setTitle(this.lessonTitle + " (0/0)"); } } private void showCard() { if(this.cards.size() > 0) { if(this.flip) { textView.setText(cards.get(this.cardIndex).getBackText()); this.textLocale = this.lesson.getBackLocale(); } else { textView.setText(cards.get(this.cardIndex).getFaceText()); this.textLocale = this.lesson.getFaceLocale(); } if(cards.get(this.cardIndex).getMarked()) { this.textView.setTextColor(this.getResources().getColor(R.color.orange)); } else { this.textView.setTextColor(this.getResources().getColor(R.color.white)); } } else { showNoCardsMessage(); } initNavigation(); } public void nextCard(View view) { this.flip = this.showBackFirst; if(( this.cardIndex + 1) < this.cardCount) { this.cardIndex++; } showCard(); } public void prevCard(View view) { this.flip = this.showBackFirst; if(this.cardIndex > 0) { this.cardIndex--; } showCard(); } public void flipCard(View view) { this.flip = !this.flip; showCard(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. if(KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_MENU)) { getMenuInflater().inflate(R.menu.card, menu); for (int i = 0; i < menu.size(); i++) { MenuItem item = menu.getItem(i); int id = item.getItemId(); switch (id) { case R.id.action_goto: break; case R.id.action_showmarked: item.setChecked(this.showMarked); break; case R.id.action_shuffle: item.setChecked(this.shuffled); break; case R.id.action_showbackfirst: item.setChecked(this.showBackFirst); break; } } } else { // no hardware menu button getMenuInflater().inflate(R.menu.card_nomenubutton, menu); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); switch (id) { case R.id.action_goto: goTo(); break; case R.id.action_showmarked: if(!this.showMarked && (countMarked() < 1)) { Toast.makeText(this, R.string.msg_no_marked_cards, Toast.LENGTH_LONG).show(); } else { this.showMarked = !this.showMarked; item.setChecked(this.showMarked); showMarkedCards(); } return true; case R.id.action_shuffle: this.shuffled = !this.shuffled; item.setChecked(this.shuffled); shuffleCards(); return true; case R.id.action_showbackfirst: this.showBackFirst = !this.showBackFirst; item.setChecked(this.showBackFirst); showBackFirst(); return true; case R.id.action_options: showOptionsDialog(); return true; case R.id.action_help: showHelp(); return true; case R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); } private void showOptionsDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.action_options); final CharSequence[] options = { this.getResources().getString(R.string.action_shuffle), this.getResources().getString(R.string.action_showmarked), this.getResources().getString(R.string.action_show_back_first) }; final boolean[] checkedOptions = {this.shuffled, this.showMarked, this.showBackFirst}; builder.setMultiChoiceItems(options, checkedOptions, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i, boolean b) { return; } }); // Set the action buttons builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { shuffled = checkedOptions[0]; showMarked = checkedOptions[1]; showBackFirst = checkedOptions[2]; try { initCards(); showCard(); } catch(DataException de) { Log.e(TAG, de.getMessage()); } } }); builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { return; } }); builder.show(); } /** * Build and show position selection dialog and navigate to selected position */ private void goTo() { final AlertDialog.Builder gotoDialog = new AlertDialog.Builder(this); gotoDialog.setTitle(R.string.action_goto); final NumberPicker numberPicker = new NumberPicker(this); numberPicker.setMaxValue(this.cardCount); numberPicker.setMinValue(1); numberPicker.setValue(this.cardIndex + 1); gotoDialog.setView(numberPicker); // Set the action buttons gotoDialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { // User clicked OK, so save the mSelectedItems results somewhere // or return them to the component that opened the dialog cardIndex = numberPicker.getValue() - 1; showCard(); } }); gotoDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { return; } }); gotoDialog.show(); } private void showBackFirst() { try { initCards(); this.flip = this.showBackFirst; if(this.cards.size() > 0) { showCard(); } else { showNoCardsMessage(); } } catch(DataException de) { Log.e(TAG, de.getMessage()); this.textView.setText(de.getMessage()); } } private void shuffleCards() { this.forceRewind = true; try { initCards(); if(this.cards.size() > 0) { showCard(); } else { showNoCardsMessage(); initNavigation(); } } catch(DataException de) { Log.e(TAG, de.getMessage()); this.textView.setText(de.getMessage()); } } private void showMarkedCards() { this.forceRewind = true; try { initCards(); if(this.cards.size() > 0) { showCard(); return; } else { showNoCardsMessage(); initNavigation(); } } catch(DataException de) { Log.e(TAG, de.getMessage()); this.textView.setText(de.getMessage()); } initNavigation(); } private void markCard() { Card card = this.cards.get(this.cardIndex); card.setMarked(!card.getMarked()); if(card.isMarked()) { if(!this.markedCardsIdSet.contains(card.getId())) { this.markedCardsIdSet.add(card.getId()); } } else { if (this.markedCardsIdSet.contains(card.getId())) { this.markedCardsIdSet.remove(card.getId()); } } showCard(); } private void showHelp() { Intent helpIntent = new Intent(this, HelpActivity.class); startActivity(helpIntent); } private void showNoCardsMessage() { this.textView.setTextColor(Color.parseColor("#FFFFFF")); this.textView.setText(R.string.msg_no_cards); } private void textToSpeech(View view, String locale, String text) { try { ttsUrls = TextToSpeech.textToSpeech(text, locale); ttsCounter = 0; } catch(UnsupportedEncodingException uex) { Log.e(TAG, uex.getMessage()); Toast.makeText(getApplicationContext(), "Text-to-speech error: " + uex.getMessage(), Toast.LENGTH_LONG).show(); } if(ttsUrls.size() > 0) { playSound(); } else { Toast.makeText(getApplicationContext(), "Cannot prepare text-to-speech request", Toast.LENGTH_LONG).show(); } } private void playSound() { this.btnPlay.setCompoundDrawablesWithIntrinsicBounds( this.getResources().getDrawable(R.drawable.ic_action_pause), null, null, null); this.btnPlay.setEnabled(false); mediaPlayer.reset(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { ttsCounter++; if(ttsCounter < ttsUrls.size()) { playSound(); } else { refreshPlayer(); } } }); mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(getApplicationContext(), "MediaPlayer error: " + what, Toast.LENGTH_LONG).show(); refreshPlayer(); return true; } }); try { mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(ttsUrls.get(ttsCounter)); // HashMap<String, String> headers = new HashMap<String, String>(); // headers.put("Accept-Language", this.textLocale.replace("_", "-")); // mediaPlayer.setDataSource(this, Uri.parse(url), headers); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start(); } catch(Exception ex) { Toast.makeText(getApplicationContext(), "MediaPlayer error: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG).show(); Log.e(TAG, ex.getLocalizedMessage()); refreshPlayer(); } } private void refreshPlayer() { ttsUrls = new ArrayList<String>(); ttsCounter = 0; this.btnPlay.setCompoundDrawablesWithIntrinsicBounds( this.getResources().getDrawable(R.drawable.ic_action_play), null, null, null); btnPlay.setEnabled(true); mediaPlayer.reset(); } }