package org.madore.android.unicodeMap; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.Formatter; import java.util.regex.Pattern; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.Menu; import android.view.MenuItem; import android.view.MenuInflater; import android.view.KeyEvent; import android.widget.*; import android.text.ClipboardManager; import android.app.ListActivity; import android.app.Dialog; import android.app.ProgressDialog; public final class UnicodeMapActivity extends ListActivity { protected UnicodeDatabase db; protected ListView lv; protected static abstract class Display { protected String title; protected SoftReference> listCacheRef; public Display(String title) { this.title = title; } public final String getTitle() { return this.title; } public void setListCache(List list) { this.listCacheRef = new SoftReference>(list); } public List getListCache() { if ( this.listCacheRef != null ) return this.listCacheRef.get(); else return null; } } protected static class RootDisplay extends Display { public RootDisplay() { super(null); } } protected static class RangeDisplay extends Display { protected int from; protected int to; protected int limit; public RangeDisplay(String title, int from, int to, int limit) { super(title); this.from = from; this.to = to; this.limit = limit; } public RangeDisplay(UnicodeRangeable rng, int limit) { super(rng.getDescr()); this.from = rng.getFrom(); this.to = rng.getTo(); this.limit = limit; } public int getFrom() { return this.from; } public int getTo() { return this.to; } public int getLimit() { return this.limit; } } protected static class SearchDisplay extends Display { protected String like; protected int limit; protected int sizeHint; public SearchDisplay(String title, String like, int limit) { super(title); this.like = like; this.limit = limit; this.sizeHint = 128; } public void setSizeHint(int hint) { this.sizeHint = hint; } public String getLike() { return this.like; } public int getLimit() { return this.limit; } public int getSizeHint() { return this.sizeHint; } } protected static class DisplayAndPosition { protected Display disp; protected int selPosition; protected int yOffset; public DisplayAndPosition(Display disp, int selPosition, int yOffset) { this.disp = disp; this.selPosition = selPosition; this.yOffset = yOffset; } public Display getDisp() { return this.disp; } public int getSelPosition() { return this.selPosition; } public int getYOffset() { return this.yOffset; } } protected Display curDisp; protected final List dispHistory = new ArrayList(10); protected EditText textForm; protected void launchPopulation() { final ProgressDialog progress = new ProgressDialog(this); progress.setOwnerActivity(this); progress.setTitle(R.string.loading_database_title); progress.setMessage(getResources().getText(R.string.loading_database_msg)); progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progress.setCancelable(false); progress.show(); final Handler handler = new Handler() { public void handleMessage(Message msg) { int done = msg.getData().getInt("done"); int total = msg.getData().getInt("total"); progress.setMax(total); progress.setProgress(done); if ( done >= total ) progress.dismiss(); } }; final Thread thr = new Thread() { public void run() { final UnicodeDatabase writeDb = new UnicodeDatabase(UnicodeMapActivity.this); writeDb.populate(handler); } }; thr.start(); } protected void saveDisplay(View selView, int selPosition) { lv.clearTextFilter(); final int yOffset; if ( selView != null ) yOffset = selView.getTop(); else yOffset = 0; dispHistory.add(new DisplayAndPosition(curDisp, selPosition, yOffset)); } protected void saveDisplay() { lv.clearTextFilter(); int position = lv.getSelectedItemPosition(); int position0 = lv.getFirstVisiblePosition(); if ( position == AdapterView.INVALID_POSITION ) position = position0; View view = lv.getChildAt(position-position0); final int yOffset; if ( view != null ) yOffset = view.getTop(); else yOffset = 0; dispHistory.add(new DisplayAndPosition(curDisp, position, yOffset)); } protected void doDisplay(Display disp, boolean isNew) { curDisp = disp; List list = disp.getListCache(); UnicodeArrayAdapter adapter; if ( list != null ) { android.util.Log.v("UnicodeMapActivity", "list was retrieved from its cache"); adapter = new UnicodeArrayAdapter(this, list); setListAdapter(adapter); } else if ( disp instanceof RootDisplay ) { list = new ArrayList(UnicodeCharacter.Range.values().length); // list.add(new UnicodeRangeable() { // public int getFrom() { return 0x4DF8; } // public int getTo() { return 0x4E10; } // public String getDescr() { return "TEST"; } // }); for ( UnicodeCharacter.Range rng : UnicodeCharacter.Range.values() ) list.add(rng); disp.setListCache(list); adapter = new UnicodeArrayAdapter(this, list); setListAdapter(adapter); } else if ( disp instanceof RangeDisplay ) { int from = ((RangeDisplay)disp).getFrom(); int to = ((RangeDisplay)disp).getTo(); int count = db.countRange(from,to); if ( count > 1024 ) { list = new ArrayList(count/128 + 1); int from0 = from&(~127); int to0 = ((to-1)|127)+1; for ( int base=from0 ; baseto?to:base+128; final String str = String.format("%04X\u2013%04X", from1, to1-1); list.add(new UnicodeRangeable() { public int getFrom() { return from1; } public int getTo() { return to1; } public String getDescr() { return str; } }); } } else { list = new ArrayList(count); for ( UnicodeCharacter ch : db.getRange(from,to) ) list.add(ch); } disp.setListCache(list); adapter = new UnicodeArrayAdapter(this, list); setListAdapter(adapter); } else if ( disp instanceof SearchDisplay ) { String s = ((SearchDisplay)disp).getLike(); int limit = ((SearchDisplay)disp).getLimit(); int sizeHint = ((SearchDisplay)disp).getSizeHint(); list = new ArrayList(sizeHint); for ( UnicodeCharacter ch : db.searchNames(s,limit+1) ) list.add(ch); disp.setListCache(list); int size = list.size(); ((SearchDisplay)disp).setSizeHint(size); boolean overflowed = ( size > limit ); if ( overflowed ) list.remove(list.size()-1); adapter = new UnicodeArrayAdapter(this, list); setListAdapter(adapter); if ( overflowed && isNew ) { String str = getResources().getString(R.string.list_too_long); Toast.makeText(UnicodeMapActivity.this, String.format(str, limit), Toast.LENGTH_SHORT).show(); } } else throw new AssertionError("unknown UnicodeMapActivity.Display"); String title = disp.getTitle(); if ( title != null ) setTitle(String.format(getResources().getString(R.string.app_name_spec), title)); else setTitle(getResources().getString(R.string.app_name)); } final static int rangeLimit = 1024; protected class MapItemClickListener implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { public void onItemClick(AdapterView parent, View view, int position, long id) { UnicodeDisplayable it = (UnicodeDisplayable)parent.getItemAtPosition(position); if ( it instanceof UnicodeCharacter ) textForm.append(((UnicodeCharacter)it).getChar()); else if ( it instanceof UnicodeRangeable ) { Display newDisp = new RangeDisplay((UnicodeRangeable)it, rangeLimit); saveDisplay(view, position); doDisplay(newDisp, true); } else throw new AssertionError("unknown UnicodeDisplayable"); } public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { UnicodeDisplayable it = (UnicodeDisplayable)parent.getItemAtPosition(position); if ( it instanceof UnicodeCharacter ) { UnicodeCharacter itch = (UnicodeCharacter)it; Dialog dialog = new Dialog(UnicodeMapActivity.this); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setOwnerActivity(UnicodeMapActivity.this); dialog.setContentView(R.layout.char_details); dialog.setCancelable(true); TextView text; text = (TextView) dialog.findViewById(R.id.detailsLabel); text.setText(itch.getLabel()); text = (TextView) dialog.findViewById(R.id.encodingLabel); StringBuilder s = new StringBuilder(); Formatter fmt = new Formatter(s); fmt.format("UTF-8:"); byte[] bytes; bytes = UnicodeCharacter.toUtf8(itch.getChar()); for ( int i=0 ; i oldDispHistory = (List)testament; dispHistory.clear(); dispHistory.addAll(oldDispHistory); assert(dispHistory.size() > 0); DisplayAndPosition saved = dispHistory.remove(dispHistory.size()-1); doDisplay(saved.getDisp(), false); lv.setSelectionFromTop(saved.getSelPosition(), saved.getYOffset()); } else doDisplay(new RootDisplay(), true); final ClipboardManager cmgr = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); final Button btn = (Button) findViewById(R.id.copyButton); textForm = (EditText) findViewById(R.id.edit); btn.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { cmgr.setText(textForm.getText()); Toast.makeText(UnicodeMapActivity.this, R.string.copied_toast, Toast.LENGTH_SHORT).show(); } }); lv.setTextFilterEnabled(true); MapItemClickListener listener = new MapItemClickListener(); lv.setOnItemClickListener(listener); lv.setOnItemLongClickListener(listener); } @Override public void onDestroy() { db.close(); db = null; super.onDestroy(); } @Override public Object onRetainNonConfigurationInstance() { saveDisplay(); return dispHistory; } final static int searchLimit = 1000; protected void doSearch(String s) { Display newDisp = new SearchDisplay(getResources().getString(R.string.search_results), s, searchLimit); saveDisplay(); doDisplay(newDisp, true); } protected void querySearch() { final Dialog dialog = new Dialog(UnicodeMapActivity.this); // dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setTitle(R.string.search_title); dialog.setOwnerActivity(this); dialog.setContentView(R.layout.search_dialog); dialog.setCancelable(true); dialog.getWindow().setLayout(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); final EditText textForm = (EditText) dialog.findViewById(R.id.searchTerm); textForm.setOnKeyListener(new View.OnKeyListener() { public boolean onKey(View view, int keyCode, KeyEvent event) { if ( ( event.getAction() == KeyEvent.ACTION_DOWN ) && ( keyCode == KeyEvent.KEYCODE_ENTER ) ) { String s = textForm.getText().toString(); if ( ! Pattern.matches("^\\s*$", s) ) doSearch(s); dialog.dismiss(); return true; } return false; } }); final Button btn = (Button) dialog.findViewById(R.id.searchButton); btn.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { String s = textForm.getText().toString(); if ( ! Pattern.matches("^\\s*$", s) ) { doSearch(s); dialog.dismiss(); } } }); dialog.show(); } protected void displayAbout() { final Dialog dialog = new Dialog(UnicodeMapActivity.this); dialog.setTitle(R.string.about_title); dialog.setOwnerActivity(this); dialog.setContentView(R.layout.about_layout); dialog.setCancelable(true); final Button btn = (Button) dialog.findViewById(R.id.aboutOk); btn.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { dialog.dismiss(); } }); dialog.show(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ( ( keyCode == KeyEvent.KEYCODE_BACK ) && dispHistory.size() > 0 ) { DisplayAndPosition saved = dispHistory.remove(dispHistory.size()-1); doDisplay(saved.getDisp(), false); lv.setSelectionFromTop(saved.getSelPosition(), saved.getYOffset()); return true; } else if ( keyCode == KeyEvent.KEYCODE_SEARCH ) { querySearch(); return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { case R.id.menuSearch: querySearch(); return true; case R.id.menuAbout: displayAbout(); return true; default: return super.onContextItemSelected(item); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.options_menu, menu); return true; } }