// 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.CreateSavedTraceSelectionOptions;
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 SDSavedTraceChooser extends AndNavBaseActivity implements OSMConstants{
// ===========================================================
// Constants
// ===========================================================
protected static final int REQUESTCODE_DDMAP = 0x1337;
protected static String EXTERNAL_STORAGE_BASEDIRECTORY;
protected static final int DIALOG_SHOWSELECTEDTRACEOPTIONS_ID = 0;
protected static final int DIALOG_RENAMESELECTEDTRACE_ID = DIALOG_SHOWSELECTEDTRACEOPTIONS_ID + 1;
// ===========================================================
// Fields
// ===========================================================
protected Bundle bundleCreatedWith;
protected ListView mSavedTracesList;
protected SavedTraceItem mSelectedSavedTrace;
// ===========================================================
// 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_savedtracechooser);
this.bundleCreatedWith = this.getIntent().getExtras();
this.mSavedTracesList = (ListView)this.findViewById(R.id.list_savedtraces);
/* Set empty view for the list. */
final TextView empty = new TextView(this);
empty.setText(R.string.list_empty);
this.mSavedTracesList.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 traces-directory exists.
new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDTRACES_PATH).mkdirs();
initListView();
updateTraceListItems();
}
applyTopMenuButtonListeners();
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods from SuperClass/Interfaces
// ===========================================================
@Override
protected Dialog onCreateDialog(final int id) {
switch(id){
case DIALOG_RENAMESELECTEDTRACE_ID:
return CommonDialogFactory.createInputDialog(this, R.string.dlg_sd_savedtracechooser_contextmenu_rename_hint, new CommonCallbackAdapter<String>(){
@Override
public void onSuccess(final String result) {
final String oldFilename = SDSavedTraceChooser.this.mSelectedSavedTrace.mFileName;
if(result != null){
try{
final boolean success = new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDTRACES_PATH + oldFilename).renameTo(new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDTRACES_PATH + result));
if(success){
updateTraceListItems();
Toast.makeText(SDSavedTraceChooser.this, R.string.dlg_sd_savedtracechooser_contextmenu_rename_success, Toast.LENGTH_LONG).show();
}else{
Toast.makeText(SDSavedTraceChooser.this, R.string.dlg_sd_savedtracechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show();
}
}catch(final Throwable t){
Toast.makeText(SDSavedTraceChooser.this, R.string.dlg_sd_savedtracechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(SDSavedTraceChooser.this, R.string.dlg_sd_savedtracechooser_contextmenu_rename_fail, Toast.LENGTH_LONG).show();
}
}
});
case DIALOG_SHOWSELECTEDTRACEOPTIONS_ID:
return CommonDialogFactory.createSavedTraceSelectionOptions(this, new CommonCallbackAdapter<CreateSavedTraceSelectionOptions>(){
@Override
public void onSuccess(final CreateSavedTraceSelectionOptions result) {
final String filename = SDSavedTraceChooser.this.mSelectedSavedTrace.mFileName;
switch(result){
case SHARE_TRAILMAPPING:
/* TODO Trace-Sharing */
break;
case INFORMATION:
/* TODO Extract Information from Trace, like number of points, etc... */
break;
case RENAME:
showDialog(DIALOG_RENAMESELECTEDTRACE_ID);
break;
case USE:
/* Proceed to next screen. */
// TODO advance to WhereAmI
final int mode = SDSavedTraceChooser.this.bundleCreatedWith.getInt(MODE_SD);
switch(mode){
case MODE_SD_DESTINATION:
final Intent directIntent = new Intent(SDSavedTraceChooser.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);
SDSavedTraceChooser.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_SELECTEDSAVEDTRACE_ID = "state_selectedsavedtrace_id";
@Override
protected void onSaveInstanceState(final Bundle icicle) {
super.onSaveInstanceState(icicle);
icicle.putParcelable(STATE_SELECTEDSAVEDTRACE_ID, this.mSelectedSavedTrace);
}
@Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
this.mSelectedSavedTrace = savedInstanceState.getParcelable(STATE_SELECTEDSAVEDTRACE_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_savedtracechooser_back)) {
@Override
public void onClicked(final View me) {
if (SDSavedTraceChooser.super.mMenuVoiceEnabled) {
MediaPlayer.create(SDSavedTraceChooser.this, R.raw.close).start();
}
/* Back one level. */
SDSavedTraceChooser.this.setResult(SUBACTIVITY_RESULTCODE_UP_ONE_LEVEL);
SDSavedTraceChooser.this.finish();
}
};
/* Set Listener for Close-Button. */
new OnClickOnFocusChangedListenerAdapter(this.findViewById(R.id.ibtn_sd_savedtracechooser_close)) {
@Override
public void onClicked(final View me) {
if (SDSavedTraceChooser.super.mMenuVoiceEnabled) {
MediaPlayer.create(SDSavedTraceChooser.this, R.raw.close).start();
}
/*
* Set ResultCode that the calling activity knows that we want
* to go back to the Base-Menu
*/
SDSavedTraceChooser.this.setResult(SUBACTIVITY_RESULTCODE_CHAINCLOSE_QUITTED);
SDSavedTraceChooser.this.finish();
}
};
}
private void updateTraceListItems() {
final ArrayList<SavedTraceItem> savedTraceItems = new ArrayList<SavedTraceItem>();
final SavedTraceListAdapter sla = new SavedTraceListAdapter(SDSavedTraceChooser.this);
final ProgressDialog pd = ProgressDialog.show(this, getString(R.string.dlg_sd_savedtracechooser_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_savedtracechooser_loading_progress);
new UserTask<Void, Integer, Void>(){
@Override
public Void doInBackground(final Void... params) {
try{
final File sdRoot = new File(EXTERNAL_STORAGE_BASEDIRECTORY + SDCARD_SAVEDTRACES_PATH);
final String[] traceFiles = sdRoot.list();
if (traceFiles != null) {
final int routeCount = traceFiles.length;
for (int i = 0; i < routeCount; i++) {
savedTraceItems.add(new SavedTraceItem(traceFiles[i]));
publishProgress(i, routeCount);
}
}
/* Adapt the list to the Adapter. */
sla.setListItems(savedTraceItems);/* Orders by name, ascending. */
}catch(final Exception e){
Exceptor.e("SavedTraceChooser-Exception", e, SDSavedTraceChooser.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. */
SDSavedTraceChooser.this.mSavedTracesList.setAdapter(sla);
pd.dismiss();
}
}.execute();
}
protected void initListView() {
this.mSavedTracesList.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(final AdapterView<?> parent, final View v, final int position, final long id) {
SDSavedTraceChooser.this.mSelectedSavedTrace = (SavedTraceItem)parent.getAdapter().getItem(position);
showDialog(DIALOG_SHOWSELECTEDTRACEOPTIONS_ID);
}
});
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
private static class SavedTraceItem implements Comparable<SavedTraceItem>, Parcelable {
protected final String mFileName;
private SavedTraceItem(final String pFileName) {
this.mFileName = pFileName;
}
@Override
public int compareTo(final SavedTraceItem 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<SavedTraceItem> CREATOR = new Parcelable.Creator<SavedTraceItem>() {
public SavedTraceItem createFromParcel(final Parcel in) {
return readFromParcel(in);
}
public SavedTraceItem[] newArray(final int size) {
return new SavedTraceItem[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel out, final int arg1) {
out.writeString(this.mFileName);
}
private static SavedTraceItem readFromParcel(final Parcel in){
return new SavedTraceItem(in.readString());
}
}
private class SavedTraceListItemView extends LinearLayout{
private final TextView mTVName;
public SavedTraceListItemView(final Context context, final SavedTraceItem 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 SavedTraceListAdapter extends BaseAdapter implements FastScrollView.SectionIndexer {
/** Remember our context so we can use it when constructing views. */
private final Context mContext;
private List<SavedTraceItem> mItems = new ArrayList<SavedTraceItem>();
private String[] mAlphabet;
public SavedTraceListAdapter(final Context context) {
this.mContext = context;
initAlphabet(context);
}
public void addItem(final SavedTraceItem it) {
this.mItems.add(it);
Collections.sort(this.mItems);
}
public void setListItems(final List<SavedTraceItem> 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) {
SavedTraceListItemView btv;
if (convertView == null) {
btv = new SavedTraceListItemView(this.mContext, this.mItems.get(position));
} else { // Reuse/Overwrite the View passed
// We are assuming(!) that it is castable!
btv = (SavedTraceListItemView) 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 SavedTraceItem(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));
}
}
}
}