package us.achromaticmetaphor.imcktg; import java.io.IOException; import java.util.LinkedList; import java.util.Locale; import java.util.concurrent.Semaphore; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.speech.tts.TextToSpeech; import android.view.View; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; public class ConfirmContacts extends Activity implements TextToSpeech.OnInitListener { private static final String extrakeyprefix = "us.achromaticmetaphor.imcktg.ConfirmContacts"; public static final String extrakeySelection = extrakeyprefix + ".selection"; public static final String extrakeyTonestring = extrakeyprefix + ".tonestring"; public static final String extrakeyFordefault = extrakeyprefix + ".forDefault"; public static final String extrakeyFilename = extrakeyprefix + ".filename"; private int outstandingTones; private TextToSpeech tts; private String previewText; private ProgressDialog pdia; private LinkedList<String> failedTones; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_confirm_contacts); tts = new TextToSpeech(this, this); if (getIntent().getBooleanExtra(extrakeyFordefault, false)) previewText = getIntent().getStringExtra(extrakeyTonestring); else { long [] selection = getIntent().getLongArrayExtra(extrakeySelection); if (selection.length == 0) previewText = "preview"; else previewText = nameForContact(contactUriForID(selection[0])); } ((SeekBar) findViewById(R.id.WPM_input)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ((TextView) findViewById(R.id.WPM_hint)).setText("" + wpm() + " wpm"); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); ((SeekBar) findViewById(R.id.FREQ_input)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { ((TextView) findViewById(R.id.FREQ_hint)).setText("" + freqRescaled(20, 4410) + "Hz / " + freqNote().toUpperCase(Locale.getDefault()) + freqOctave()); } @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) {} }); } @Override protected void onDestroy() { super.onDestroy(); tts.shutdown(); } private class Listener implements AsyncGenerateMorseTones.Listener { String name; public Listener(String name) { this.name = name; } @Override public void onFinished(boolean succeeded) { if (! succeeded) addFailedTone(name); decrementOutstandingTones(); } } private void checkDone() { if (outstandingTones <= 0) { pdia.dismiss(); if (failedTones.size() > 0) { StringBuilder sb = new StringBuilder("Some ringtones could not be generated:\n"); for (String name : failedTones) sb.append(" ").append(name).append("\n"); new AlertDialog.Builder(this) .setMessage(sb.toString()) .setTitle("Something untoward has occurred.") .setPositiveButton("Okay", new DialogInterface.OnClickListener() { public void onClick(DialogInterface di, int b) { finish(); }}) .show(); } else finish(); } } public synchronized void decrementOutstandingTones() { outstandingTones--; checkDone(); } private synchronized void addFailedTone(String name) { failedTones.add(name); } private Uri contactUriForID(long id) { return Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, "" + id); } private String nameForContact(Uri contacturi) { Cursor cursor = getContentResolver().query(contacturi, new String [] {ContactsContract.Contacts.DISPLAY_NAME}, null, null, null); cursor.moveToNext(); return cursor.getString(0); } public void generateAndAssignTones(View view) { generateAndAssignTones(spinnerGen()); } public void generateAndAssignTones(ToneGenerator gen) { pdia = ProgressDialog.show(this, "Generating", "Please wait", true, false); failedTones = new LinkedList<String>(); if (getIntent().getBooleanExtra(extrakeyFordefault, false)) { final String tonestring = getIntent().getStringExtra(extrakeyTonestring); outstandingTones = 1; final AsyncGenerateMorseTones async = new AsyncGenerateMorseTones(); async.execute(new AsyncGenerateMorseTones.Params(new Listener("default tone"), this, tonestring, gen, null, getIntent())); } else { final long [] selection = getIntent().getLongArrayExtra(extrakeySelection); outstandingTones = selection.length; for (long id : selection) { final Uri contacturi = contactUriForID(id); final String name = nameForContact(contacturi); final AsyncGenerateMorseTones async = new AsyncGenerateMorseTones(); async.execute(new AsyncGenerateMorseTones.Params(new Listener(name), this, name, gen, contacturi, getIntent())); } } checkDone(); } public ToneGenerator spinnerGen() { String sel = (String) ((Spinner) findViewById(R.id.format_spinner)).getSelectedItem(); return sel.equals("Morse (WAV)") ? pcmGen() : sel.equals("Morse (iMelody)") ? imyGen() : ttsGen(); } private class OAFCL implements AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnCompletionListener { private final AudioManager aman; private final Tone preview; public OAFCL(AudioManager aman, Tone preview) { this.aman = aman; this.preview = preview; } @Override public void onAudioFocusChange(int focusChange) {} @Override public void onCompletion(MediaPlayer player) { player.release(); aman.abandonAudioFocus(this); preview.delete(ConfirmContacts.this); } } private int freqRescaled(int min, int max) { ProgressBar pbar = ((ProgressBar) findViewById(R.id.FREQ_input)); final int freq = pbar.getProgress(); final int pmax = pbar.getMax(); return min + ((max - min) * freq / pmax); } private float freqRescaled() { return (freqRescaled(20, 4410) + 2195) / 2195.0f; } private int wpm() { return 1 + ((ProgressBar) findViewById(R.id.WPM_input)).getProgress(); } private ToneGenerator pcmGen() { return new MorsePCM(freqRescaled(20, 4410), wpm(), repeatCount()); } private int repeatCount() { try { return Integer.parseInt(((TextView) findViewById(R.id.RC_input)).getText().toString()); } catch (NumberFormatException nfe) { return 0; } } private int freqTone() { return freqRescaled(0, 62); } private int freqOctave() { return freqTone() / 7; } private String freqNote() { return "cdefgab".substring(freqTone() % 7).substring(0, 1); } private ToneGenerator imyGen() { return new MorseIMelody(freqOctave(), freqNote(), wpm(), repeatCount()); } private ToneGenerator ttsGen() { return new TTS(tts, freqRescaled(), wpm() / 20.0f, repeatCount()); } public void previewTone(View view) { previewTone(spinnerGen()); } public void previewTone(ToneGenerator gen) { AudioManager aman = (AudioManager) getSystemService(AUDIO_SERVICE); MediaPlayer player; try { Intent i = new Intent(); i.putExtra(extrakeyFilename, Tone.tmpFilename()); Tone preview = Tone.generateTone(this, previewText, gen, i); player = MediaPlayer.create(this, preview.contentUri()); OAFCL oafcl = new OAFCL(aman, preview); aman.requestAudioFocus(oafcl, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); player.setOnCompletionListener(oafcl); player.start(); } catch (IOException e) { } } @Override public void onInit(int status) { if (status == TextToSpeech.SUCCESS) enableTTS(); } private void enableTTS() { } }