de.grobox.liberario.fragments.DirectionsFragment.java Source code

Java tutorial

Introduction

Here is the source code for de.grobox.liberario.fragments.DirectionsFragment.java

Source

/*    Transportr
 *    Copyright (C) 2013 - 2016 Torsten Grote
 *
 *    This program is Free Software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.grobox.liberario.fragments;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.CardView;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter;
import com.mikepenz.fastadapter.items.AbstractItem;
import com.mikepenz.fastadapter.utils.ViewHolderFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;

import de.grobox.liberario.R;
import de.grobox.liberario.activities.TripsActivity;
import de.grobox.liberario.data.LocationDb;
import de.grobox.liberario.favorites.trips.FavoriteTripItem;
import de.grobox.liberario.fragments.ProductDialogFragment.OnProductsChangedListener;
import de.grobox.liberario.locations.AmbiguousLocationActivity;
import de.grobox.liberario.locations.LocationGpsView;
import de.grobox.liberario.locations.LocationView;
import de.grobox.liberario.locations.WrapLocation;
import de.grobox.liberario.networks.NetworkProviderFactory;
import de.grobox.liberario.settings.Preferences;
import de.grobox.liberario.tasks.AsyncQueryTripsTask;
import de.grobox.liberario.tasks.AsyncQueryTripsTask.TripHandler;
import de.grobox.liberario.ui.TimeAndDateView;
import de.schildbach.pte.NetworkProvider;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryTripsResult;

import static android.graphics.PorterDuff.Mode.SRC_ATOP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static de.grobox.liberario.activities.MainActivity.PR_ACCESS_FINE_LOCATION_DIRECTIONS;
import static de.grobox.liberario.data.FavoritesDb.updateFavoriteTrip;
import static de.grobox.liberario.favorites.locations.FavoriteLocation.FavLocationType.FROM;
import static de.grobox.liberario.favorites.locations.FavoriteLocation.FavLocationType.TO;
import static de.grobox.liberario.favorites.locations.FavoriteLocation.FavLocationType.VIA;
import static de.grobox.liberario.settings.Preferences.SHOW_ADV_DIRECTIONS;
import static de.grobox.liberario.utils.TransportrUtils.fixToolbarIcon;
import static de.grobox.liberario.utils.TransportrUtils.getButtonIconColor;
import static de.grobox.liberario.utils.TransportrUtils.getDrawableForLocation;
import static de.grobox.liberario.utils.TransportrUtils.getDrawableForProduct;
import static de.grobox.liberario.utils.TransportrUtils.getTintedDrawable;
import static de.grobox.liberario.utils.TransportrUtils.getToolbarDrawable;

@Deprecated
public class DirectionsFragment extends TransportrFragment implements TripHandler, OnProductsChangedListener {

    public final static String TAG = "de.grobox.liberario.directions";
    public final static String TASK_BRING_ME_HOME = "bring_me_home";
    private ProgressDialog pd;

    private DirectionsViewHolder ui;
    private AsyncQueryTripsTask mAfterGpsTask = null;
    private EnumSet<Product> products = EnumSet.allOf(Product.class);
    private FastItemAdapter<ProductItem> productsAdapter;
    private boolean showingMore = false;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_directions, container, false);

        ui = new DirectionsViewHolder(v);

        ui.from.setType(FROM);
        ui.from.setCaller(PR_ACCESS_FINE_LOCATION_DIRECTIONS);
        LocationGpsView.LocationGpsListener listener = new LocationGpsView.LocationGpsListener() {
            @Override
            public void activateGPS() {
            }

            @Override
            public void deactivateGPS() {
            }

            @Override
            public void onLocationChanged(Location location) {
                if (pd != null) {
                    pd.dismiss();
                }

                // query for trips if user pressed search already and we just have been waiting for the location
                if (mAfterGpsTask != null) {
                    mAfterGpsTask.setFrom(location);
                    mAfterGpsTask.execute();
                }
            }
        };
        ui.from.setLocationGpsListener(listener);

        ui.via.setType(VIA);
        ui.to.setType(TO);

        // Set Type to Departure=True
        ui.type.setTag(true);

        // Trip Date Type Spinner (departure or arrival)
        ui.type.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                setType(!(boolean) ui.type.getTag());
            }
        });

        // Products
        productsAdapter = new FastItemAdapter<>();
        ui.products.setHasFixedSize(true);
        ui.products.setAdapter(productsAdapter);
        products = Preferences.getProducts(getContext());
        onProductsChanged(products);
        ui.products.getBackground().setColorFilter(getButtonIconColor(getActivity()), SRC_ATOP);
        ui.products.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // onClickListener doesn't work for some reasion, so use touch instead
                if (event.getAction() == ACTION_UP) {
                    showProductDialog();
                    return true;
                }
                return false;
            }
        });

        ui.search.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                search();
            }
        });

        ui.fav_trips_separator_star.setColorFilter(getButtonIconColor(getActivity()));
        ui.fav_trips_separator_line.setBackgroundColor(getButtonIconColor(getActivity()));

        ui.favourites.setLayoutManager(new LinearLayoutManager(getContext()));

        if (!Preferences.getPref(getActivity(), SHOW_ADV_DIRECTIONS, false)) {
            // don't animate here, since this method is called on each fragment change from the drawer
            showLess(false);
        } else {
            showingMore = true;
        }

        return v;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (savedInstanceState != null) {
            setType(savedInstanceState.getBoolean("type"));
            Serializable tmpProducts = savedInstanceState.getSerializable("products");
            if (tmpProducts instanceof EnumSet) {
                //noinspection unchecked
                products = (EnumSet<Product>) tmpProducts;
                onProductsChanged(products);
            }
            // re-attach fragment listener
            Fragment fragment = getActivity().getSupportFragmentManager()
                    .findFragmentByTag(ProductDialogFragment.TAG);
            if (fragment != null && fragment.getUserVisibleHint()) {
                ((ProductDialogFragment) fragment).setOnProductsChangedListener(this);
            }
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (ui != null) {
            outState.putBoolean("type", (boolean) ui.type.getTag());
            outState.putSerializable("products", products);
        }
    }

    @Override
    public void onResume() {
        super.onResume();

        mAfterGpsTask = null;

        refreshAutocomplete(false);

        // check if there's an intent for us and if so, act on it
        processIntent();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Inflate the menu items for use in the action bar
        inflater.inflate(R.menu.directions, menu);

        // in some cases getActivity() and getContext() can be null, so get it somewhere else
        Context context = getContext();

        MenuItem expandItem = menu.findItem(R.id.action_navigation_expand);
        if (ui.products.getVisibility() == GONE) {
            expandItem.setIcon(getToolbarDrawable(context, R.drawable.ic_action_navigation_unfold_more));
        } else {
            expandItem.setIcon(getToolbarDrawable(context, R.drawable.ic_action_navigation_unfold_less));
        }

        MenuItem swapItem = menu.findItem(R.id.action_swap_locations);
        fixToolbarIcon(context, swapItem);

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle presses on the action bar items
        switch (item.getItemId()) {
        case R.id.action_navigation_expand:
            if (ui.products.getVisibility() == GONE) {
                item.setIcon(getToolbarDrawable(getContext(), R.drawable.ic_action_navigation_unfold_less));
                Preferences.setPref(getActivity(), SHOW_ADV_DIRECTIONS, true);
                showMore(true);
            } else {
                item.setIcon(getToolbarDrawable(getContext(), R.drawable.ic_action_navigation_unfold_more));
                Preferences.setPref(getActivity(), SHOW_ADV_DIRECTIONS, false);
                showLess(true);
            }

            return true;
        case R.id.action_swap_locations:
            swapLocations();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onTripRetrieved(QueryTripsResult result) {
        if (getContext() == null) {
            // FIXME: This is not a proper solution, we need to use a loader instead
            return;
        }

        if (result.status == QueryTripsResult.Status.OK && result.trips != null && result.trips.size() > 0) {
            Log.d(getClass().getSimpleName(), result.toString());

            Intent intent = new Intent(getContext(), TripsActivity.class);
            intent.putExtra("de.schildbach.pte.dto.QueryTripsResult", result);
            fillIntent(intent);
            startActivity(intent);
        } else if (result.status == QueryTripsResult.Status.AMBIGUOUS) {
            Log.d(getClass().getSimpleName(), "QueryTripsResult is AMBIGUOUS");

            Intent intent = new Intent(getContext(), AmbiguousLocationActivity.class);
            intent.putExtra("de.schildbach.pte.dto.QueryTripsResult", result);
            fillIntent(intent);
            startActivity(intent);
        } else {
            Toast.makeText(getContext(), getContext().getResources().getString(R.string.error_no_trips_found),
                    LENGTH_LONG).show();
        }
    }

    @Override
    public void onTripRetrievalError(String error) {
    }

    @Override
    public void onProductsChanged(EnumSet<Product> products) {
        this.products = products;
        productsAdapter.clear();
        for (Product product : products) {
            productsAdapter.add(new ProductItem(product));
        }
    }

    private void showProductDialog() {
        ProductDialogFragment productFragment = ProductDialogFragment.newInstance(products);
        productFragment.setOnProductsChangedListener(DirectionsFragment.this);
        FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
        productFragment.show(ft, ProductDialogFragment.TAG);
    }

    private void search() {
        NetworkProvider np = NetworkProviderFactory.provider(Preferences.getNetworkId(getActivity()));
        if (!np.hasCapabilities(NetworkProvider.Capability.TRIPS)) {
            Toast.makeText(getActivity(), getString(R.string.error_no_trips_capability), LENGTH_SHORT).show();
            return;
        }

        if (ui.to == null && ui.from == null) {
            // the activity is most likely being recreated after configuration change, don't search again
            return;
        }

        if (ui.to.isChangingHome()) {
            // we are currently in a state of changing home in the to field, a search is not possible
            return;
        }

        AsyncQueryTripsTask query_trips = new AsyncQueryTripsTask(getActivity(), this);

        // check and set to location
        if (checkLocation(ui.to)) {
            query_trips.setTo(ui.to.getLocation());
        } else {
            Toast.makeText(getActivity(), getResources().getString(R.string.error_invalid_to), LENGTH_SHORT).show();
            return;
        }

        // check and set via location
        if (checkLocation(ui.via)) {
            query_trips.setVia(ui.via.getLocation());
        }

        // check and set from location
        if (ui.from.isSearching()) {
            if (ui.from.getLocation() != null) {
                query_trips.setFrom(ui.from.getLocation());
            } else {
                mAfterGpsTask = query_trips;

                pd = new ProgressDialog(getActivity());
                pd.setMessage(getResources().getString(R.string.stations_searching_position));
                pd.setCancelable(false);
                pd.setIndeterminate(true);
                pd.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mAfterGpsTask = null;
                        dialog.dismiss();
                    }
                });
                pd.show();
            }
        } else {
            if (checkLocation(ui.from)) {
                query_trips.setFrom(ui.from.getLocation());
            } else {
                Toast.makeText(getActivity(), getString(R.string.error_invalid_from), LENGTH_SHORT).show();
                return;
            }
        }

        // remember trip
        updateFavoriteTrip(getActivity(),
                new FavoriteTripItem(ui.from.getLocation(), ui.via.getLocation(), ui.to.getLocation()));

        // set date
        query_trips.setDate(ui.date.getDate());

        // set departure to true of first item is selected in spinner
        query_trips.setDeparture((boolean) ui.type.getTag());

        // set products
        query_trips.setProducts(products);

        // don't execute if we still have to wait for GPS position
        if (mAfterGpsTask != null)
            return;

        query_trips.execute();
    }

    private void processIntent() {
        final Intent intent = getActivity().getIntent();
        if (intent != null) {
            final String action = intent.getAction();
            if (action != null && action.equals(TAG)) {
                WrapLocation from, via, to;
                boolean search;
                Date date;
                String eSpecial = (String) intent.getSerializableExtra("special");
                if (eSpecial != null && eSpecial.equals(TASK_BRING_ME_HOME)) {
                    from = new WrapLocation(WrapLocation.WrapType.GPS);
                    to = new WrapLocation(WrapLocation.WrapType.HOME);
                    search = true;
                } else {
                    from = (WrapLocation) intent.getSerializableExtra("from");
                    to = (WrapLocation) intent.getSerializableExtra("to");
                    search = intent.getBooleanExtra("search", false);
                }
                via = (WrapLocation) intent.getSerializableExtra("via");
                date = (Date) intent.getSerializableExtra("date");

                if (search)
                    searchFromTo(from, via, to, date);
                else
                    presetFromTo(from, via, to, date);
            }

            // remove the intent (and clear its action) since it was already processed
            // and should not be processed again
            intent.setAction(null);
            getActivity().setIntent(null);
        }
    }

    private void presetFromTo(WrapLocation wfrom, WrapLocation wvia, WrapLocation wto, Date date) {

        Location from, via, to;

        // unwrap wfrom
        if (wfrom != null) {
            from = wfrom.getLocation();
            if (wfrom.getType() == WrapLocation.WrapType.GPS) {
                activateGPS();
                from = null;
            }
        } else {
            from = null;
        }

        // handle from-location
        if (ui.from != null && from != null) {
            ui.from.setLocation(from);
        }

        // unwrap wvia
        if (wvia != null) {
            via = wvia.getLocation();
        } else {
            via = null;
        }

        // handle via-location
        if (ui.via != null) {
            ui.via.setLocation(via);
            if (via != null && ui.products.getVisibility() == GONE) {
                // if there's a via location, make sure to show it in the UI
                showMore(true);
            }
        }

        // unwrap wto
        if (wto != null) {
            to = wto.getLocation();
            if (wto.getType() == WrapLocation.WrapType.HOME) {
                to = null;
                ui.to.setWrapLocation(wto);
            }
        } else {
            to = null;
        }

        // handle to-location
        if (ui.to != null && to != null) {
            ui.to.setLocation(to);
        }

        // handle date
        if (date != null) {
            ui.date.setDate(date);
        }
    }

    private void searchFromTo(WrapLocation from, WrapLocation via, WrapLocation to, Date date) {
        presetFromTo(from, via, to, date);
        search();
    }

    private void refreshAutocomplete(boolean always) {
        if (ui.from != null) {
            if (always)
                ui.from.reset();
            else
                ui.from.resetIfEmpty();
        }
        if (ui.to != null) {
            if (always)
                ui.to.reset();
            else
                ui.to.resetIfEmpty();
        }
    }

    public void activateGPS() {
        if (ui.from != null) {
            ui.from.activateGPS();
        }
    }

    private Boolean checkLocation(LocationView loc_view) {
        Location loc = loc_view.getLocation();

        if (loc == null) {
            // no location was selected by user
            if (loc_view.getText() != null && loc_view.getText().length() > 0) {
                // no location selected, but text entered. So let's try create locations from text
                loc_view.setLocation(new Location(LocationType.ANY, null, loc_view.getText(), loc_view.getText()));

                return true;
            }
            return false;
        }
        // we have a location, so make it a favorite
        else {
            LocationDb.updateFavLocation(getActivity(), loc, loc_view.getType());
        }

        return true;
    }

    private void showMore(boolean animate) {
        showingMore = true;
        ui.via.setVisibility(VISIBLE);
        ui.products.setVisibility(VISIBLE);
        ui.fav_trips_separator.setVisibility(VISIBLE);

        if (animate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            DisplayMetrics dm = new DisplayMetrics();
            getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
        }
    }

    private void showLess(boolean animate) {
        showingMore = false;
        ui.via.setLocation(null);
        ui.via.setVisibility(GONE);
        ui.products.setVisibility(GONE);
    }

    private void setType(boolean departure) {
        if (departure) {
            ui.type.setText(getString(R.string.trip_dep));
            ui.type.setTag(true);
        } else {
            ui.type.setText(getString(R.string.trip_arr));
            ui.type.setTag(false);
        }
    }

    private void swapLocations() {
        Animation slideUp = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f);

        slideUp.setDuration(400);
        slideUp.setFillAfter(true);
        slideUp.setFillEnabled(true);
        ui.to.startAnimation(slideUp);

        Animation slideDown = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 1.0f);

        slideDown.setDuration(400);
        slideDown.setFillAfter(true);
        slideDown.setFillEnabled(true);
        ui.from.startAnimation(slideDown);

        slideUp.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                // swap location objects
                Location tmp = ui.to.getLocation();
                if (!ui.from.isSearching()) {
                    ui.to.setLocation(ui.from.getLocation(),
                            getDrawableForLocation(getContext(), ui.from.getLocation()));
                } else {
                    // TODO: GPS currently only supports from location, so don't swap it for now
                    ui.to.clearLocation();
                }
                ui.from.setLocation(tmp, getDrawableForLocation(getContext(), tmp));

                ui.from.clearAnimation();
                ui.to.clearAnimation();
            }
        });
    }

    private void fillIntent(Intent intent) {
        intent.putExtra("de.schildbach.pte.dto.Trip.from", ui.from.getLocation());
        intent.putExtra("de.schildbach.pte.dto.Trip.via", ui.via.getLocation());
        intent.putExtra("de.schildbach.pte.dto.Trip.to", ui.to.getLocation());
        intent.putExtra("de.schildbach.pte.dto.Trip.date", ui.date.getDate());
        intent.putExtra("de.schildbach.pte.dto.Trip.departure", (boolean) ui.type.getTag());
        intent.putExtra("de.schildbach.pte.dto.Trip.products", new ArrayList<>(products));
    }

    private static class DirectionsViewHolder {
        LocationGpsView from;
        LocationView via;
        LocationView to;
        Button type;
        TimeAndDateView date;
        RecyclerView products;
        Button search;
        RecyclerView favourites;
        CardView no_favourites;
        LinearLayout fav_trips_separator;
        View fav_trips_separator_line;
        ImageView fav_trips_separator_star;

        DirectionsViewHolder(View mView) {
            from = (LocationGpsView) mView.findViewById(R.id.fromLocation);
            via = (LocationView) mView.findViewById(R.id.viaLocation);
            to = (LocationView) mView.findViewById(R.id.toLocation);

            type = (Button) mView.findViewById(R.id.dateType);
            date = (TimeAndDateView) mView.findViewById(R.id.dateView);

            products = (RecyclerView) mView.findViewById(R.id.productsList);
            search = (Button) mView.findViewById(R.id.searchButton);

            favourites = (RecyclerView) mView.findViewById(R.id.favourites);
            no_favourites = (CardView) mView.findViewById(R.id.no_favourites);
            fav_trips_separator = (LinearLayout) mView.findViewById(R.id.fav_trips_separator);
            fav_trips_separator = (LinearLayout) mView.findViewById(R.id.fav_trips_separator);
            fav_trips_separator_line = mView.findViewById(R.id.fav_trips_separator_line);
            fav_trips_separator_star = (ImageView) mView.findViewById(R.id.fav_trips_separator_star);
        }
    }

    class ProductItem extends AbstractItem<ProductDialogFragment.ProductItem, ProductItem.ViewHolder> {
        private final ViewHolderFactory<? extends ProductItem.ViewHolder> FACTORY = new ProductItem.ItemFactory();
        private final Product product;

        ProductItem(Product product) {
            this.product = product;
        }

        @Override
        public int getType() {
            return product.ordinal();
        }

        @Override
        public int getLayoutRes() {
            return R.layout.item_product;
        }

        @Override
        public void bindView(final ProductItem.ViewHolder ui, List<Object> payloads) {
            super.bindView(ui, payloads);
            ui.image.setImageDrawable(getTintedDrawable(getContext(), getDrawableForProduct(product)));
            ui.image.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    showProductDialog();
                }
            });
        }

        @Override
        public ViewHolderFactory<? extends ProductItem.ViewHolder> getFactory() {
            return FACTORY;
        }

        class ItemFactory implements ViewHolderFactory<ProductItem.ViewHolder> {
            public ProductItem.ViewHolder create(View v) {
                return new ProductItem.ViewHolder(v);
            }
        }

        class ViewHolder extends RecyclerView.ViewHolder {
            private ImageView image;

            ViewHolder(View v) {
                super(v);
                image = (ImageView) v.findViewById(R.id.productView);
            }
        }
    }

}