Java tutorial
/* * Copyright (C) 2009 Johan Nilsson <http://markupartist.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.markupartist.sthlmtraveling; import android.app.Dialog; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.location.Location; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ToggleButton; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.crashlytics.android.Crashlytics; import com.markupartist.sthlmtraveling.provider.JourneysProvider.Journey.Journeys; import com.markupartist.sthlmtraveling.provider.planner.JourneyQuery; import com.markupartist.sthlmtraveling.provider.planner.Planner; import com.markupartist.sthlmtraveling.provider.planner.Planner.IntermediateStop; import com.markupartist.sthlmtraveling.provider.planner.Planner.SubTrip; import com.markupartist.sthlmtraveling.provider.planner.Planner.Trip2; import com.markupartist.sthlmtraveling.ui.view.SmsTicketDialog; import com.markupartist.sthlmtraveling.utils.Analytics; import com.markupartist.sthlmtraveling.utils.DateTimeUtil; import org.json.JSONException; import java.io.IOException; import java.util.List; public class RouteDetailActivity extends BaseListActivity { public static final String TAG = "RouteDetailActivity"; public static final String EXTRA_JOURNEY_TRIP = "sthlmtraveling.intent.action.JOURNEY_TRIP"; public static final String EXTRA_JOURNEY_QUERY = "sthlmtraveling.intent.action.JOURNEY_QUERY"; private static final int DIALOG_BUY_SMS_TICKET = 1; private Trip2 mTrip; private JourneyQuery mJourneyQuery; private SubTripAdapter mSubTripAdapter; private ImageButton mFavoriteButton; private ActionBar mActionBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.route_details_list); registerScreen("Route details"); Bundle extras = getIntent().getExtras(); mTrip = (Trip2) getLastNonConfigurationInstance(); if (mTrip == null) { mTrip = extras.getParcelable(EXTRA_JOURNEY_TRIP); } mJourneyQuery = extras.getParcelable(EXTRA_JOURNEY_QUERY); mActionBar = initActionBar(); updateStartAndEndPointViews(mJourneyQuery); View headerView = getLayoutInflater().inflate(R.layout.route_header_details, null); TextView timeView = (TextView) headerView.findViewById(R.id.route_date_time); timeView.setText(getString(R.string.time_to, mTrip.getDurationText(), mTrip.destination.getCleanName())); if (mTrip.canBuySmsTicket()) { View buySmsTicketView = headerView.findViewById(R.id.route_buy_ticket); buySmsTicketView.setVisibility(View.VISIBLE); buySmsTicketView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Analytics.getInstance(RouteDetailActivity.this).event("Ticket", "Click on zone"); showDialog(DIALOG_BUY_SMS_TICKET); } }); TextView zoneView = (TextView) headerView.findViewById(R.id.route_zones); zoneView.setText(mTrip.tariffZones); } getListView().addHeaderView(headerView, null, false); onRouteDetailsResult(mTrip); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getSupportMenuInflater(); inflater.inflate(R.menu.actionbar_route_detail, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem starItem = menu.findItem(R.id.actionbar_item_star); if (isStarredJourney(mJourneyQuery)) { starItem.setIcon(R.drawable.ic_action_star_on); } else { starItem.setIcon(R.drawable.ic_action_star_off); } if (!mTrip.canBuySmsTicket()) { menu.removeItem(R.id.actionbar_item_sms); } return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionbar_item_time: Intent departuresIntent = new Intent(this, DeparturesActivity.class); departuresIntent.putExtra(DeparturesActivity.EXTRA_SITE_NAME, mTrip.origin.name); startActivity(departuresIntent); return true; case R.id.actionbar_item_sms: if (mTrip.canBuySmsTicket()) { Analytics.getInstance(this).event("Ticket", "Click on ab"); showDialog(DIALOG_BUY_SMS_TICKET); } else { Toast.makeText(this, R.string.sms_ticket_notice_disabled, Toast.LENGTH_LONG).show(); } return true; case R.id.actionbar_item_star: handleStarAction(); supportInvalidateOptionsMenu(); return true; } return super.onOptionsItemSelected(item); } /** * Helper that returns the my location text representation. If the {@link Location} * is set the accuracy will also be appended. * @param location the stop * @return a text representation of my location */ private CharSequence getLocationName(Planner.Location location) { if (location == null) { return "Unknown"; } if (location.isMyLocation()) { return getText(R.string.my_location); } return location.name; /*if (location.getLocation() != null) { string = String.format("%s (%sm)", string, location.getLocation().getAccuracy()); }*/ } /** * Called before this activity is destroyed, returns the previous details. * This data is used if the screen is rotated. Then we don't need to ask for * the data again. * * @return route details */ @Override public Object onRetainNonConfigurationInstance() { return mTrip; } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); restoreLocalState(savedInstanceState); } /** * Restores any local state, if any. * @param savedInstanceState the bundle containing the saved state */ private void restoreLocalState(Bundle savedInstanceState) { } @Override protected void onDestroy() { super.onDestroy(); } /** * Called when there is results to display. * @param trip the route details */ public void onRouteDetailsResult(Trip2 trip) { getListView().addFooterView(createFooterView(trip)); setListAdapter(mSubTripAdapter); mTrip = trip; } private View createFooterView(final Trip2 trip) { mSubTripAdapter = new SubTripAdapter(this, trip.subTrips); int numSubTrips = trip.subTrips.size(); final SubTrip lastSubTrip = trip.subTrips.get(numSubTrips - 1); // todo: make sure this is safe. View convertView = getLayoutInflater().inflate(R.layout.trip_row_stop_layout, null); Button nameView = (Button) convertView.findViewById(R.id.trip_stop_title); nameView.setText(getLocationName(lastSubTrip.destination)); nameView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Planner.Location location = lastSubTrip.destination; if (location.hasLocation()) { startActivity(createViewOnMapIntent(mTrip, mJourneyQuery, location)); } else { Toast.makeText(RouteDetailActivity.this, "Missing geo data", Toast.LENGTH_LONG).show(); } } }); View endSegment = convertView.findViewById(R.id.trip_line_segment_end); endSegment.setVisibility(View.VISIBLE); convertView.findViewById(R.id.trip_layout_intermediate_stop).setVisibility(View.GONE); // TextView arrivalTimeView = (TextView) convertView.findViewById(R.id.trip_arrival_time); // arrivalTimeView.setVisibility(View.GONE); TextView departureTimeView = (TextView) convertView.findViewById(R.id.trip_departure_time); departureTimeView.setText(lastSubTrip.arrivalTime); convertView.findViewById(R.id.trip_intermediate_stops_layout).setVisibility(View.GONE); return convertView; } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_BUY_SMS_TICKET: return SmsTicketDialog.createDialog(this, mTrip.tariffZones); } return null; } @Override public boolean onSearchRequested() { Intent i = new Intent(this, StartActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); return true; } private Intent createViewOnMapIntent(Planner.Trip2 trip, JourneyQuery query, Planner.Location location) { Intent intent = new Intent(this, ViewOnMapActivity.class); intent.putExtra(ViewOnMapActivity.EXTRA_TRIP, trip); intent.putExtra(ViewOnMapActivity.EXTRA_JOURNEY_QUERY, query); intent.putExtra(ViewOnMapActivity.EXTRA_LOCATION, location); return intent; } private boolean isStarredJourney(JourneyQuery journeyQuery) { String json; try { json = mJourneyQuery.toJson(false).toString(); } catch (JSONException e) { Log.e(TAG, "Failed to convert journey to a json document."); return false; } String[] projection = new String[] { Journeys.JOURNEY_DATA, }; Uri uri = Journeys.CONTENT_URI; String selection = Journeys.STARRED + " = ? AND " + Journeys.JOURNEY_DATA + " = ?"; String[] selectionArgs = new String[] { "1", json }; Cursor cursor = managedQuery(uri, projection, selection, selectionArgs, null); return cursor.getCount() > 0; } private View.OnClickListener mLocationClickListener = new View.OnClickListener() { @Override public void onClick(View v) { Planner.Location l = (Planner.Location) v.getTag(); if (l.hasLocation()) { startActivity(createViewOnMapIntent(mTrip, mJourneyQuery, l)); } else { Toast.makeText(RouteDetailActivity.this, "Missing geo data", Toast.LENGTH_LONG).show(); } } }; private void handleStarAction() { String json; try { json = mJourneyQuery.toJson(false).toString(); } catch (JSONException e) { Log.e(TAG, "Failed to convert journey to a json document."); return; } ContentValues values = new ContentValues(); values.put(Journeys.JOURNEY_DATA, json); Uri uri = Journeys.CONTENT_URI; String where = Journeys.JOURNEY_DATA + "= ?"; String[] selectionArgs = new String[] { json }; if (isStarredJourney(mJourneyQuery)) { values.put(Journeys.STARRED, "0"); getContentResolver().update(uri, values, where, selectionArgs); } else { values.put(Journeys.STARRED, "1"); int affectedRows = getContentResolver().update(uri, values, where, selectionArgs); if (affectedRows <= 0) { values.put(Journeys.STARRED, "1"); getContentResolver().insert(Journeys.CONTENT_URI, values); } } } /** * A not at all optimized adapter for showing a route based on a list of sub trips. */ private class SubTripAdapter extends ArrayAdapter<SubTrip> { private LayoutInflater mInflater; private boolean mIsFetchingSubTrips; public SubTripAdapter(Context context, List<SubTrip> objects) { super(context, R.layout.route_details_row, objects); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public boolean areAllItemsEnabled() { return false; } @Override public View getView(final int position, View convertView, ViewGroup parent) { final SubTrip subTrip = getItem(position); convertView = mInflater.inflate(R.layout.trip_row_stop_layout, null); if (position > 0) { final SubTrip previousSubTrip = getItem(position - 1); convertView.findViewById(R.id.trip_intermediate_departure_time).setVisibility(View.GONE); TextView descView = (TextView) convertView.findViewById(R.id.trip_intermediate_stop_title); descView.setTextSize(12); descView.setText(getLocationName(previousSubTrip.destination)); TextView arrivalView = (TextView) convertView.findViewById(R.id.trip_intermediate_arrival_time); arrivalView.setText(previousSubTrip.arrivalTime); } else { convertView.findViewById(R.id.trip_layout_intermediate_stop).setVisibility(View.GONE); convertView.findViewById(R.id.trip_line_segment_start).setVisibility(View.VISIBLE); } Button nameView = (Button) convertView.findViewById(R.id.trip_stop_title); nameView.setText(getLocationName(subTrip.origin)); nameView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Planner.Location location = subTrip.origin; if (location.hasLocation()) { startActivity(createViewOnMapIntent(mTrip, mJourneyQuery, location)); } else { Toast.makeText(getContext(), "Missing geo data", Toast.LENGTH_LONG).show(); } } }); /* View startSegment = convertView.findViewById(R.id.trip_line_segment_start); if (position > 0) { final SubTrip prevSubTrip = getItem(position - 1); arrivalTimeView.setText(prevSubTrip.arrivalTime); startSegment.setVisibility(View.GONE); } else { startSegment.setVisibility(View.VISIBLE); arrivalTimeView.setVisibility(View.GONE); } */ TextView departureTimeView = (TextView) convertView.findViewById(R.id.trip_departure_time); departureTimeView.setText(subTrip.departureTime); // Add description data ViewStub descriptionStub = (ViewStub) convertView.findViewById(R.id.trip_description_stub); View descriptionLayout = descriptionStub.inflate(); TextView descriptionView = (TextView) descriptionLayout.findViewById(R.id.trip_description); descriptionView.setText(createDescription(subTrip)); ImageView descriptionIcon = (ImageView) descriptionLayout.findViewById(R.id.trip_description_icon); descriptionIcon.setImageResource(subTrip.transport.getImageResource()); //descriptionView.setCompoundDrawablesWithIntrinsicBounds(subTrip.transport.getImageResource(), 0, 0, 0); inflateMessages(subTrip, convertView); inflateIntermediate(subTrip, position, convertView); return convertView; } private void inflateIntermediate(final SubTrip subTrip, final int position, final View convertView) { final ToggleButton btnIntermediateStops = (ToggleButton) convertView .findViewById(R.id.trip_btn_intermediate_stops); CharSequence durationText = DateTimeUtil.formatDuration(getResources(), subTrip.getDurationMillis()); btnIntermediateStops.setText(durationText); btnIntermediateStops.setTextOn(durationText); btnIntermediateStops.setTextOff(durationText); if ("Walk".equals(subTrip.transport.type)) { btnIntermediateStops.setVisibility(View.GONE); } final LinearLayout stopsLayout = (LinearLayout) convertView.findViewById(R.id.trip_intermediate_stops); btnIntermediateStops.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { if (stopsLayout.getChildCount() == 0 && subTrip.intermediateStop.size() == 0 && !mIsFetchingSubTrips) { mIsFetchingSubTrips = true; new GetIntermediateStopTask(getContext(), new GetIntermediateStopTask.OnResult() { @Override public void onResult(SubTrip st) { if (st.intermediateStop.isEmpty()) { stopsLayout.addView( inflateText(getText(R.string.no_intermediate_stops), stopsLayout)); } for (IntermediateStop is : st.intermediateStop) { stopsLayout.addView(inflateIntermediateStop(is, stopsLayout)); } mTrip.subTrips.set(position, st); mIsFetchingSubTrips = false; } }).execute(subTrip, mJourneyQuery); } else if (stopsLayout.getChildCount() == 0 && subTrip.intermediateStop.size() > 0) { for (IntermediateStop is : subTrip.intermediateStop) { stopsLayout.addView(inflateIntermediateStop(is, stopsLayout)); } } stopsLayout.setVisibility(View.VISIBLE); } else { stopsLayout.setVisibility(View.GONE); } } }); } private View inflateText(CharSequence text, LinearLayout stopsLayout) { View view = mInflater.inflate(R.layout.trip_row_intermediate_stop, stopsLayout, false); TextView descView = (TextView) view.findViewById(R.id.trip_intermediate_stop_title); descView.setTextSize(12); descView.setText(text); view.findViewById(R.id.trip_intermediate_line_segment_stop).setVisibility(View.GONE); return view; } private View inflateIntermediateStop(IntermediateStop stop, LinearLayout stopsLayout) { View view = mInflater.inflate(R.layout.trip_row_intermediate_stop, stopsLayout, false); view.findViewById(R.id.trip_intermediate_departure_time).setVisibility(View.GONE); TextView descView = (TextView) view.findViewById(R.id.trip_intermediate_stop_title); descView.setTextSize(12); descView.setText(stop.location.name); TextView arrivalView = (TextView) view.findViewById(R.id.trip_intermediate_arrival_time); arrivalView.setText(stop.arrivalTime); return view; } private void inflateMessages(final SubTrip subTrip, final View convertView) { LinearLayout messagesLayout = (LinearLayout) convertView.findViewById(R.id.trip_messages); if (!subTrip.remarks.isEmpty()) { for (String message : subTrip.remarks) { messagesLayout.addView(inflateMessage("remark", message, messagesLayout)); } } if (!subTrip.rtuMessages.isEmpty()) { for (String message : subTrip.rtuMessages) { messagesLayout.addView(inflateMessage("rtu", message, messagesLayout)); } } if (!subTrip.mt6Messages.isEmpty()) { for (String message : subTrip.mt6Messages) { messagesLayout.addView(inflateMessage("mt6", message, messagesLayout)); } } } private View inflateMessage(String type, String message, LinearLayout messagesLayout) { View view = mInflater.inflate(R.layout.trip_row_message, messagesLayout, false); TextView messageView = (TextView) view.findViewById(R.id.trip_message); messageView.setText(message); return view; } private CharSequence createDescription(final SubTrip subTrip) { CharSequence description; if ("Walk".equals(subTrip.transport.type)) { description = getString(R.string.trip_description_walk); } else { description = getString(R.string.trip_description_normal, subTrip.transport.name, subTrip.transport.towards); } return description; } } private static class GetIntermediateStopTask extends AsyncTask<Object, Void, SubTrip> { private final Context mContext; GetIntermediateStopTask.OnResult mCallback; public GetIntermediateStopTask(Context context, GetIntermediateStopTask.OnResult onResult) { mContext = context; mCallback = onResult; } @Override protected SubTrip doInBackground(Object... params) { SubTrip subTrip = (SubTrip) params[0]; try { Planner.getInstance().addIntermediateStops(mContext, subTrip, (JourneyQuery) params[1]); } catch (IOException e) { Crashlytics.logException(e); } return subTrip; } @Override protected void onPostExecute(SubTrip result) { mCallback.onResult(result); } private static interface OnResult { public void onResult(SubTrip subTrip); } } }