// Created by plusminus on 20:58:26 - 09.12.2008 package org.androad.ui.sd; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.androad.R; import org.androad.exc.Exceptor; import org.androad.osm.util.Util; import org.androad.osm.util.constants.OSMConstants; import org.androad.preferences.Preferences; import org.androad.ui.AndNavBaseActivity; import org.androad.ui.common.CommonCallbackAdapter; import org.androad.ui.common.CommonDialogFactory; import org.androad.ui.common.OnClickOnFocusChangedListenerAdapter; import org.androad.ui.common.CommonDialogFactory.CreateSavedRouteSelectionOptions; import org.androad.ui.common.views.FastScrollView; import org.androad.ui.map.OpenStreetDDMap; import org.androad.util.UserTask; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; public class SDSavedRouteChooser extends AndNavBaseActivity implements OSMConstants{ // =========================================================== // Constants // =========================================================== protected static final int REQUESTCODE_DDMAP = 0x1337; protected static String EXTERNAL_STORAGE_BASEDIRECTORY; protected static final int DIALOG_SHOWSELECTEDROUTEOPTIONS_ID = 0; protected static final int DIALOG_RENAMESELECTEDROUTE_ID = DIALOG_SHOWSELECTEDROUTEOPTIONS_ID + 1; // =========================================================== // Fields // =========================================================== protected Bundle bundleCreatedWith; protected ListView mSavedRoutesList; protected SavedRouteItem mSelectedSavedRoute; // =========================================================== // Constructors // =========================================================== /** Called when the activity is first created. */ @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle, true); // We need DataState-Info Preferences.applySharedSettings(this); this.setContentView(R.layout.sd_savedroutechooser); this.bundleCreatedWith = this.getIntent().getExtras(); this.mSavedRoutesList = (ListView)this.findViewById(R.id.list_savedroutes); /* Set empty view for the list. */ final TextView empty = new TextView(this); empty.setText(R.string.list_empty); this.mSavedRoutesList.setEmptyView(empty); if(!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){ /* Should not happen as the activity is not selectable in SDMainChoose, when no external media is inserted. */ }else{ EXTERNAL_STORAGE_BASEDIRECTORY = Util.getAndRoadExternalStoragePath(); // Ensure the routes-directory exists. new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDROUTES_PATH).mkdirs(); initListView(); updateSavedRouteListItems(); } applyTopMenuButtonListeners(); } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods from SuperClass/Interfaces // =========================================================== @Override protected Dialog onCreateDialog(final int id) { switch(id){ case DIALOG_RENAMESELECTEDROUTE_ID: return CommonDialogFactory.createInputDialog(this, R.string.dlg_sd_savedroutechooser_contextmenu_rename_hint, new CommonCallbackAdapter<String>(){ @Override public void onSuccess(final String result) { final String oldFilename = SDSavedRouteChooser.this.mSelectedSavedRoute.mFileName; if(result != null){ try{ final boolean success = new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDROUTES_PATH + oldFilename).renameTo(new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDROUTES_PATH + result)); if(success){ updateSavedRouteListItems(); Toast.makeText(SDSavedRouteChooser.this, R.string.dlg_sd_savedroutechooser_contextmenu_rename_success, Toast.LENGTH_LONG).show(); }else{ Toast.makeText(SDSavedRouteChooser.this, R.string.dlg_sd_savedroutechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show(); } }catch(final Throwable t){ Toast.makeText(SDSavedRouteChooser.this, R.string.dlg_sd_savedroutechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show(); } }else{ Toast.makeText(SDSavedRouteChooser.this, R.string.dlg_sd_savedroutechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show(); } } }); case DIALOG_SHOWSELECTEDROUTEOPTIONS_ID: return CommonDialogFactory.createSavedRouteSelectionOptions(this, new CommonCallbackAdapter<CreateSavedRouteSelectionOptions>(){ @Override public void onSuccess(final CreateSavedRouteSelectionOptions result) { final String filename = SDSavedRouteChooser.this.mSelectedSavedRoute.mFileName; switch(result){ case SHARE: // TODO RouteFactory.getRouteHandleFromInputStream/File break; case INFORMATION: /* TODO Extract Information from Route. */ break; case RENAME: showDialog(DIALOG_RENAMESELECTEDROUTE_ID); break; case USE: /* Proceed to next screen. */ final int mode = SDSavedRouteChooser.this.bundleCreatedWith.getInt(MODE_SD); switch(mode){ case MODE_SD_DESTINATION: final Intent directIntent = new Intent(SDSavedRouteChooser.this, OpenStreetDDMap.class); final Bundle b = new Bundle(); b.putInt(EXTRAS_MODE, EXTRAS_MODE_LOAD_SAVED_ROUTE); b.putString(EXTRAS_SAVED_ROUTE_FILENAME_ID, filename); directIntent.putExtras(b); SDSavedRouteChooser.this.startActivityForResult(directIntent, REQUESTCODE_DDMAP); break; case MODE_SD_RESOLVE: case MODE_SD_WAYPOINT: throw new IllegalArgumentException("Only MODE_SD_DESTINATION is possible."); } break; } } }); default: return null; } } private static final String STATE_SELECTEDSAVEDROUTE_ID = "state_selectedsavedroute_id"; @Override protected void onSaveInstanceState(final Bundle icicle) { super.onSaveInstanceState(icicle); icicle.putParcelable(STATE_SELECTEDSAVEDROUTE_ID, this.mSelectedSavedRoute); } @Override protected void onRestoreInstanceState(final Bundle savedInstanceState) { this.mSelectedSavedRoute = savedInstanceState.getParcelable(STATE_SELECTEDSAVEDROUTE_ID); } @Override protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { switch(resultCode){ case SUBACTIVITY_RESULTCODE_CHAINCLOSE_SUCCESS: this.setResult(SUBACTIVITY_RESULTCODE_CHAINCLOSE_SUCCESS, data); this.finish(); break; case SUBACTIVITY_RESULTCODE_CHAINCLOSE_QUITTED: this.setResult(SUBACTIVITY_RESULTCODE_CHAINCLOSE_QUITTED, data); this.finish(); break; } /* Finally call the super()-method. */ super.onActivityResult(requestCode, resultCode, data); } // =========================================================== // Methods // =========================================================== protected void applyTopMenuButtonListeners() { /* Set Listener for Back-Button. */ new OnClickOnFocusChangedListenerAdapter(this.findViewById(R.id.ibtn_sd_savedroutechooser_back)) { @Override public void onClicked(final View me) { if (SDSavedRouteChooser.super.mMenuVoiceEnabled) { MediaPlayer.create(SDSavedRouteChooser.this, R.raw.close).start(); } /* Back one level. */ SDSavedRouteChooser.this.setResult(SUBACTIVITY_RESULTCODE_UP_ONE_LEVEL); SDSavedRouteChooser.this.finish(); } }; /* Set Listener for Close-Button. */ new OnClickOnFocusChangedListenerAdapter(this.findViewById(R.id.ibtn_sd_savedroutechooser_close)) { @Override public void onClicked(final View me) { if (SDSavedRouteChooser.super.mMenuVoiceEnabled) { MediaPlayer.create(SDSavedRouteChooser.this, R.raw.close).start(); } /* * Set ResultCode that the calling activity knows that we want * to go back to the Base-Menu */ SDSavedRouteChooser.this.setResult(SUBACTIVITY_RESULTCODE_CHAINCLOSE_QUITTED); SDSavedRouteChooser.this.finish(); } }; } private void updateSavedRouteListItems() { final ArrayList<SavedRouteItem> savedRouteItems = new ArrayList<SavedRouteItem>(); final SavedRouteListAdapter sla = new SavedRouteListAdapter(SDSavedRouteChooser.this); final ProgressDialog pd = ProgressDialog.show(this, getString(R.string.dlg_sd_savedroutechooser_loading_title), getString(R.string.please_wait_a_moment), false); // TODO Make determinate, when SDK supports this. final String progressBaseString = getString(R.string.dlg_sd_savedroutechooser_loading_progress); new UserTask<Void, Integer, Void>(){ @Override public Void doInBackground(final Void... params) { try{ final File sdRoot = new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDROUTES_PATH); final String[] routeFiles = sdRoot.list(); if (routeFiles != null) { final int routeCount = routeFiles.length; for (int i = 0; i < routeCount; i++) { savedRouteItems.add(new SavedRouteItem(routeFiles[i])); publishProgress(i, routeCount); } } Collections.sort(savedRouteItems); /* Adapt the list to the Adapter. */ sla.setListItems(savedRouteItems);/* Orders by name, ascending. */ }catch(final Exception e){ Exceptor.e("SavedRouteChooser-Exception", e, SDSavedRouteChooser.this); } return null; } @Override public void onProgressUpdate(final Integer... progress) { pd.setMessage(String.format(progressBaseString, (int)(100*((float)progress[0] / progress[1])), progress[0], progress[1])); } @Override public void onPostExecute(final Void result) { /* Adapt the Adapter to the ListView. */ SDSavedRouteChooser.this.mSavedRoutesList.setAdapter(sla); pd.dismiss(); } }.execute(); } protected void initListView() { this.mSavedRoutesList.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(final AdapterView<?> parent, final View v, final int position, final long id) { SDSavedRouteChooser.this.mSelectedSavedRoute = (SavedRouteItem)parent.getAdapter().getItem(position); showDialog(DIALOG_SHOWSELECTEDROUTEOPTIONS_ID); } }); } // =========================================================== // Inner and Anonymous Classes // =========================================================== private static class SavedRouteItem implements Comparable<SavedRouteItem>, Parcelable { protected final String mFileName; private SavedRouteItem(final String pFileName) { this.mFileName = pFileName; } @Override public int compareTo(final SavedRouteItem another) { final boolean thisFirstDigit = Character.isDigit(this.mFileName.charAt(0)); final boolean otherFirstDigit = Character.isDigit(another.mFileName.charAt(0)); /* Make Characters appear above of digits(timestamps). */ if(thisFirstDigit && !otherFirstDigit) { return 1; } else if(!thisFirstDigit && otherFirstDigit) { return -1; } else if(thisFirstDigit && otherFirstDigit) { return -this.mFileName.compareToIgnoreCase(another.mFileName); /* Ascending. */ } else { return this.mFileName.compareToIgnoreCase(another.mFileName); /* Descending. */ } } // =========================================================== // Parcelable // =========================================================== public static final Parcelable.Creator<SavedRouteItem> CREATOR = new Parcelable.Creator<SavedRouteItem>() { public SavedRouteItem createFromParcel(final Parcel in) { return readFromParcel(in); } public SavedRouteItem[] newArray(final int size) { return new SavedRouteItem[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(final Parcel out, final int arg1) { out.writeString(this.mFileName); } private static SavedRouteItem readFromParcel(final Parcel in){ return new SavedRouteItem(in.readString()); } } private class SavedRouteListItemView extends LinearLayout{ private final TextView mTVName; public SavedRouteListItemView(final Context context, final SavedRouteItem aPOIItem) { super(context); this.setOrientation(VERTICAL); this.mTVName = new TextView(context); this.mTVName.setText(aPOIItem.mFileName); this.mTVName.setTextSize(TypedValue.COMPLEX_UNIT_PX, 24); this.mTVName.setPadding(10,0,20,0); addView(this.mTVName, new LinearLayout.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT)); } } private class SavedRouteListAdapter extends BaseAdapter implements FastScrollView.SectionIndexer { /** Remember our context so we can use it when constructing views. */ private final Context mContext; private List<SavedRouteItem> mItems = new ArrayList<SavedRouteItem>(); private String[] mAlphabet; public SavedRouteListAdapter(final Context context) { this.mContext = context; initAlphabet(context); } public void addItem(final SavedRouteItem it) { this.mItems.add(it); Collections.sort(this.mItems); } public void setListItems(final List<SavedRouteItem> lit) { this.mItems = lit; Collections.sort(this.mItems); } @Override public boolean isEmpty() { return this.mItems == null || this.mItems.size() == 0; } /** @return The number of items in the */ public int getCount() { return this.mItems.size(); } public Object getItem(final int position) { return this.mItems.get(position); } @Override public long getItemId(final int position) { return position; } @Override public View getView(final int position, final View convertView, final ViewGroup parent) { SavedRouteListItemView btv; if (convertView == null) { btv = new SavedRouteListItemView(this.mContext, this.mItems.get(position)); } else { // Reuse/Overwrite the View passed // We are assuming(!) that it is castable! btv = (SavedRouteListItemView) convertView; btv.mTVName.setText(this.mItems.get(position).mFileName); } return btv; } // =========================================================== // FastScrollView-Methods // =========================================================== @Override public int getPositionForSection(final int section) { final String firstChar = this.mAlphabet[section]; /* Find the index, of the firstchar within the Contact-Items */ int position = Collections.binarySearch(this.mItems, new SavedRouteItem(firstChar)); if(position < 0){ /* Negative result means the insertion-point. * See definition of Collections.binarySearch */ position = -(position + 1); } return position; } @Override public int getSectionForPosition(final int position) { return 0; } @Override public Object[] getSections() { return this.mAlphabet; } private void initAlphabet(final Context context) { final String alphabetString = context.getResources().getString(R.string.alphabet); // TODO Use Systems Alphabet! this.mAlphabet = new String[alphabetString.length()]; for (int i = 0; i < this.mAlphabet.length; i++) { this.mAlphabet[i] = String.valueOf(alphabetString.charAt(i)); } } } }