Android Open Source - meeting-app Show Results Fragment






From Project

Back to project page meeting-app.

License

The source code is released under:

Apache License

If you think the Android project meeting-app listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright 2014 Google Inc./*  ww  w  . j ava  2 s .c om*/
 *
 * 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.example.meetingapp;

import com.google.android.gms.maps.model.LatLng;

import android.annotation.SuppressLint;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.RelativeLayout;

import com.example.geocodeservice.GeocodeResponse;
import com.example.geocodeservice.GeocodeService;
import com.example.placephotos.PhotoService;
import com.example.wrappers.DistanceMatrix;
import com.example.wrappers.DistanceMatrixResponse;
import com.example.wrappers.PlaceCriteria;
import com.example.wrappers.PlaceResult;
import com.example.wrappers.PlacesResponse;
import com.example.wrappers.PlacesService;
import com.example.wrappers.RankBy;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

@SuppressLint("ValidFragment")
public class ShowResultsFragment extends Fragment {
  public static final String TAG = ShowResultsFragment.class.getSimpleName();
  
  private static final GeocodeService mGeocodeService = new GeocodeService();
  private static final PlacesService mPlacesService =
      new PlacesService("AIzaSyCtDiLiH1Qlb1PWogd8bgn8Ww8NK2_CQLk");
  private static final PhotoService mPhotoService = 
      new PhotoService("AIzaSyCtDiLiH1Qlb1PWogd8bgn8Ww8NK2_CQLk");

  public static final String PLACE_RESULTS =
      "com.example.meetingapp.PLACE_RESULTS";
  public static final String PLACE_DETAILS = "com.example.meetingapp.details";
  public static final String SHOW_MAP_TEXT = "Show Map";
  
  private Callback mCallback;
  private ModifyStateCallback mStateCallback;
  private State mState;
  private ArrayList<LocationParcel> mNearbyPlaces;
  private ArrayList<String> mAddresses;
  private ArrayAdapter<LocationParcel> mAdapter;
  
  private ListView mResultsList;
  private RelativeLayout mLayout;
  private ProgressDialog mDialog;
  private Button mButton;
  
  public interface Callback {    
    /**
     * Called once the button at the bottom of the screen is clicked.
     * 
     * @param tag Used to tag the fragment in the back stack, in case it needs
     * to be referenced later. Can be null.
     */
    public void showResultsOnMap(String tag);
    
    /**
     * Called once an item card has been clicked.
     * 
     * @param selection The details of the selected place.
     */
    public void showResult(LocationParcel selection);
  }
  
  public ShowResultsFragment (Callback callback,
      ModifyStateCallback stateCallback) {
    mCallback = callback;
    mStateCallback = stateCallback;
  }
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // Show the Up button in the action bar.
    setupActionBar();
  }
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    mLayout = (RelativeLayout) inflater.inflate(
        R.layout.show_results_fragment, null);
    
    mResultsList = (ListView) mLayout.findViewById(R.id.results_list);
    
    mButton = (Button) mLayout.findViewById(R.id.show_map);
    mButton.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View view) {
        mCallback.showResultsOnMap(TAG);
      }
    });
    
    return mLayout;
  }
  
  @Override
  public void onResume() {
    super.onResume();
    setupActionBar();
    
    /* Change the "Next Page" text as it will no longer go to the next page. */
    mButton.setText(SHOW_MAP_TEXT);
    
    mState = mStateCallback.getState();
    
    mNearbyPlaces = mState.getResults();
    mAdapter = new ResultAdapter(
        getActivity(), R.layout.result_list_item, mNearbyPlaces);
    
    /* If we already have results then don't show them! If we don't, query
     * again just in case.
     */
    if (mNearbyPlaces.size() == 0) {
      setResults(mState.getSelectedLocations());      
    } else {
      displayResults();
    }
    
    mAddresses = mState.getSelectedLocationsAsStrings();
  }
  
  @Override
  public void onPause() {
    super.onPause();
    
    mState.putResults(mNearbyPlaces);
    mStateCallback.putState(mState);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case android.R.id.home:
        // This ID represents the Home or Up button. In the case of this
        // activity, the Up button is shown. Use NavUtils to allow users
        // to navigate up one level in the application structure. For
        // more details, see the Navigation pattern on Android Design:
        //
        // http://developer.android.com/design/patterns/navigation.html#up-vs-back
        //
        NavUtils.navigateUpFromSameTask(getActivity());
        return true;
    }
    return super.onOptionsItemSelected(item);
  }
  
  /**
   * Set up the {@link android.app.ActionBar}.
   */
  private void setupActionBar() {
    getActivity().getActionBar().setTitle("Results");
  }
  
  /* Triggers generation of results for the query.
   */
  private void setResults(ArrayList<LatLng> locations) {
    LatLng centrepoint = getCentre(locations);
    String category;
    
    /* Turn the categories into an API-friendly format (lower case, and with
     * underscores instead of spaces.)
     */
    try {
      category = mState.getCategory()
          .toLowerCase(Locale.getDefault()).replace(" ", "_");
    } catch (NullPointerException noCategory) {
      category = "";
    }
  
    int minPrice = mState.getMinPrice();
    int maxPrice = mState.getMaxPrice();

    String keyword = mState.getKeywords();
    if (keyword == null) {
      keyword = "";
    }
    
    /* Reset the results so we aren't adding to an old list. */ 
    mNearbyPlaces = new ArrayList<LocationParcel>();
    LookupTask task = new LookupTask(
                                     keyword, category, minPrice, maxPrice, locations);

    task.execute(centrepoint);
  }
  
  private LatLng getCentre(ArrayList<LatLng> locations) {
    double latitudeSum = 0;
    double longitudeSum = 0;
    int numLocations = locations.size();
    
    for (LatLng location : locations) {
      latitudeSum += location.latitude;
      longitudeSum += location.longitude;
    }
    LatLng centre = new LatLng(
        latitudeSum / numLocations, longitudeSum / numLocations);
    
    if (numLocations != 0) {
      mState.putCentre(centre);
      mStateCallback.putState(mState);
    }
    
    return centre;
  }
  
  private void displayResults() {
    mAdapter = new ResultAdapter(
        getActivity(), R.layout.result_list_item, mNearbyPlaces);
    
    mResultsList.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> parent, View view, 
          int position, long id) {
        LocationParcel selection = 
            (LocationParcel) parent.getItemAtPosition(position);
        
        mState.putResult(selection);
        mStateCallback.putState(mState);
        
        mCallback.showResult(selection);
      }
    });
    mResultsList.setAdapter(mAdapter);
    
    SetPhotoTask task = new SetPhotoTask();
    task.execute();
  }
  
  public class GetAddressTask extends AsyncTask<LatLng, Void, String> {
    
    @Override
    protected String doInBackground(LatLng... params) {
      LatLng loc = params[0];
      GeocodeService.Request request = GeocodeService.request(true)
          .latlng(loc.latitude, loc.longitude)
          .build();
      
      GeocodeResponse response;
      try {
        response = mGeocodeService.getAddress(request);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      
      if (response.status.equals("ZERO_RESULTS")) {
        return null;
      } else if (response.status.equals("OVER_QUERY_LIMIT")) {
        Log.e(TAG, "This app is over the Geocoding API query limit.");
        return null;
      }
      return response.results.get(0).formatted_address;
    }
    
    @Override
    protected void onPostExecute(String address) {
      if (address != null) {
        mAddresses.add(address);
      }
    }
  }
  
  public class LookupTask extends AsyncTask<LatLng, Void, PlacesResponse> {
    private String keyword;
    private String type;
    private int minPrice;
    private int maxPrice;
    private ArrayList<LatLng> locations;

    public LookupTask(String keyword, String type, 
                      int minPrice, int maxPrice, ArrayList<LatLng> locations) {
      this.keyword = keyword.trim();
      this.type = type;
      this.locations = locations;
      this.minPrice = minPrice;
      this.maxPrice = maxPrice;
    }

    /**
     * Set the value to be used for the radius in the places search.
     * Radius size is determined by the distance of points from the centroid. 
     * @param locations the list of LatLng objects which represent all the locations to be considered.
     * @return the radius to be used. 
     */
    private int setRadius (ArrayList<LatLng> locations) {
      LatLng centre = getCentre (locations);
      int radius;
      int distance = 0;
      double[] destinations = new double[2 * locations.size ()];
      int i = 0;
      for (LatLng location: locations) {
        destinations[i] = location.latitude;
        i++;
        destinations[i] = location.longitude;
        i++;
      }

      double[] origins = {centre.latitude, centre.longitude};
      DistanceMatrix d = new DistanceMatrix ();
      DistanceMatrix.Request r = d.request (false).origins (origins).destinations (destinations).build ();
      DistanceMatrixResponse response = d.search(r);

      for (DistanceMatrixResponse.Rows.Elements element: response.rows.get(0).elements) {
        distance += element.distance.value;
      }

      distance = distance / locations.size ();
      radius = distance / 5;
      if (radius < 1000) {
        radius = 1000;
      } else if (radius > 50000) {
        radius = 50000;
      }

      return radius;
    }    

    @Override
    protected void onPreExecute() {
      if (mDialog == null) {
        mDialog = new ProgressDialog(getActivity());
        mDialog.setTitle("Loading results...");
        mDialog.setMessage("Please wait.");
        mDialog.setCancelable(false);
        mDialog.setIndeterminate(true);
        mDialog.show();
      }
    }
    
    @Override
    protected PlacesResponse doInBackground(LatLng... coords) {
      PlacesService.Request request;
      int radius = setRadius(this.locations);
      request = PlacesService.request(true)
        .radius(radius)
        .types(this.type)
        .keyword(this.keyword)
        .minPrice(this.minPrice)
        .maxPrice(this.maxPrice)
        .location(coords[0].latitude, coords[0].longitude).build();
      Log.d(TAG, "Radius set to: "+radius);
      PlacesResponse response;
      response = mPlacesService.nearbySearch(request);
      response.results = getMostSuitable(response.results);
      return response;
    }

    private List<PlaceResult> getMostSuitable (List<PlaceResult> results) {
      // Todo ask the user if they are going now to use open now criteria 
      PlaceCriteria criteria = new PlaceCriteria ();
      // Some constants (possibly subjective)
      int matches = 7;
      int rating = 4;
      int photo = 4;
      int price;
      // The following line will compile after Vanessa's code is merged 
      int openNow = mState.getOpenNow()?5:0;
      switch (this.maxPrice) {
      case 0: 
      case 1: 
      case 2:
      case 3:
        price = 5-this.maxPrice;
        break;
      case 4:
        price = 0;
        break;
      default: // invalid price 
        price = 3; // Sensible default should never happen.
        break;
      }

      criteria.setPhoto (photo).setRating (rating).setPrice (price).setOpenNow (openNow);
      if (this.type.length () > 0) {
        criteria.setTypes (4, this.type);
      }

      RankBy rank = new RankBy (criteria, results);
      return rank.rankPlaces(matches);
    }
    
    @Override
    protected void onPostExecute(PlacesResponse response) {
      if (response.results == null) {
        Log.e(TAG, "results are null!"); 
      } else {
        Log.d(TAG, "there are results! " + response.results.size() +
            " " + response.status);
      }

      List<PlaceResult> results = response.results;
      String reference = null;
      LatLng place;
      
      for (PlaceResult r: results) {
        place = new LatLng(r.geometry.location.lat, r.geometry.location.lng);
        if (r.photos == null) {
          reference = null;
        } else {
          reference = r.photos.get(0).photo_reference;
        }
        
        boolean openNow = false;
        if (r.opening_hours != null) {
          openNow = r.opening_hours.open_now;
        }
        mNearbyPlaces.add(new LocationParcel(place.latitude, place.longitude, 
            r.name, r.reference, reference, r.icon, r.rating, r.price_level,
            openNow, r.vicinity));
        Log.d(TAG, "Found " + r.name);
      }

      mDialog.dismiss();
      mDialog = null;
      displayResults();
    }
  }
  
  public class SetPhotoTask extends AsyncTask<Void, Void, Void> {
    private boolean accessibilityEnabled;
    
    public SetPhotoTask() {
      /* Set accessibilityEnabled, based on whether the accessibility service is
       * turned on or off.
       */
    }
    
    @Override
    protected Void doInBackground(Void... params) {
      for (LocationParcel parcel : mNearbyPlaces) {
        if (parcel.getPhotoreference() == null) {
          continue;
        }
        PhotoService.Request request = PhotoService.request()
            .sensor(true)
            .photoreference(parcel.getPhotoreference())
            .maxheight(400)
            .build();
        parcel.setPhoto(mPhotoService.getPhoto(request));
        
        /* Don't refresh the views if accessibility is enabled; this will
         * result in the text of the child views being re-spoken over and over.
         * Only refresh the views with photos at the very end.
         */
        if (!accessibilityEnabled) {
          publishProgress();
        }
      }
      return null;
    }
    
    @Override
    protected void onProgressUpdate(Void... argument) {
      mAdapter.notifyDataSetChanged();
    }
    
    @Override
    protected void onPostExecute(Void argument) {
      mAdapter.notifyDataSetChanged();
    }
  }
}




Java Source Code List

com.example.geocodeservice.GeocodeResponse.java
com.example.geocodeservice.GeocodeResult.java
com.example.geocodeservice.GeocodeServiceTest.java
com.example.geocodeservice.GeocodeService.java
com.example.meetingapp.LocationParcel.java
com.example.meetingapp.MainActivity.java
com.example.meetingapp.MapResultsFragment.java
com.example.meetingapp.ModifyStateCallback.java
com.example.meetingapp.OptionsFragment.java
com.example.meetingapp.PickCategoryFragment.java
com.example.meetingapp.PickLocationFragment.java
com.example.meetingapp.PlacesAutocompleteAdapter.java
com.example.meetingapp.ResultAdapter.java
com.example.meetingapp.ShowDetailsFragment.java
com.example.meetingapp.ShowResultsFragment.java
com.example.meetingapp.State.java
com.example.placedetails.DetailsResponse.java
com.example.placedetails.DetailsResult.java
com.example.placedetails.DetailsService.java
com.example.placephotos.PhotoService.java
com.example.wrappers.DistanceMatrixResponse.java
com.example.wrappers.DistanceMatrixTest.java
com.example.wrappers.DistanceMatrix.java
com.example.wrappers.LatLng.java
com.example.wrappers.PlaceAutocompletePrediction.java
com.example.wrappers.PlaceCriteria.java
com.example.wrappers.PlaceQuery.java
com.example.wrappers.PlaceResult.java
com.example.wrappers.PlaceSearch.java
com.example.wrappers.PlaceWrapperTest.java
com.example.wrappers.PlaceWrapper.java
com.example.wrappers.PlacesAutocompleteResponse.java
com.example.wrappers.PlacesAutocomplete.java
com.example.wrappers.PlacesResponse.java
com.example.wrappers.PlacesServiceTest.java
com.example.wrappers.PlacesService.java
com.example.wrappers.RankBy.java
com.example.wrappers.StaticMapTest.java
com.example.wrappers.StaticMap.java