1package com.xtremelabs.robolectric.shadows;
2
3import android.location.Address;
4import android.location.Geocoder;
5import com.xtremelabs.robolectric.internal.Implementation;
6import com.xtremelabs.robolectric.internal.Implements;
7
8import java.io.IOException;
9import java.util.ArrayList;
10import java.util.List;
11import java.util.Locale;
12
13import static com.xtremelabs.robolectric.Robolectric.shadowOf;
14
15/**
16 * A shadow for Geocoder that supports simulated responses and failures
17 */
18@SuppressWarnings({"UnusedDeclaration"})
19@Implements(Geocoder.class)
20public class ShadowGeocoder {
21    private String addressLine1;
22    private String city;
23    private String state;
24    private String zip;
25    private String countryCode;
26    private boolean wasCalled;
27    private double lastLatitude;
28    private double lastLongitude;
29    private String lastLocationName;
30    private double simulatedLatitude;
31    private double simulatedLongitude;
32    private boolean shouldSimulateGeocodeException;
33    private boolean hasLatitude;
34    private boolean hasLongitude;
35    private boolean returnNoResults = false;
36    private boolean didResolution;
37
38    @Implementation
39    public List<Address> getFromLocation(double latitude, double longitude, int maxResults) throws IOException {
40        wasCalled = true;
41        this.lastLatitude = latitude;
42        this.lastLongitude = longitude;
43        if (shouldSimulateGeocodeException) {
44            throw new IOException("Simulated geocode exception");
45        }
46        Address address = makeAddress();
47        address.setAddressLine(0, addressLine1);
48        address.setLocality(city);
49        address.setAdminArea(state);
50        address.setPostalCode(zip);
51        address.setCountryCode(countryCode);
52        return oneElementList(address);
53    }
54
55    @Implementation
56    public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException {
57        didResolution = true;
58        this.lastLocationName = locationName;
59        if (shouldSimulateGeocodeException) {
60            throw new IOException("Simulated geocode exception");
61        }
62        if (returnNoResults) {
63            return new ArrayList<Address>();
64        } else {
65            Address address = makeAddress();
66            address.setLatitude(simulatedLatitude);
67            address.setLongitude(simulatedLongitude);
68            return oneElementList(address);
69        }
70    }
71
72    private Address makeAddress() {
73        Address address = new Address(Locale.getDefault());
74        shadowOf(address).setSimulatedHasLatLong(hasLatitude, hasLongitude);
75        return address;
76    }
77
78    /**
79     * Sets up a simulated response for {@link #getFromLocation(double, double, int)}
80     *
81     * @param address     the address for the response
82     * @param city        the city for the response
83     * @param state       the state for the response
84     * @param zip         the zip code for the response
85     * @param countryCode the country code for the response
86     */
87    public void setSimulatedResponse(String address, String city, String state, String zip, String countryCode) {
88        this.addressLine1 = address;
89        this.city = city;
90        this.state = state;
91        this.zip = zip;
92        this.countryCode = countryCode;
93    }
94
95
96    /**
97     * Sets up a simulated response for {@link #getFromLocationName(String, int)}}
98     *
99     * @param lat latitude for simulated response
100     * @param lng longitude for simulated response
101     */
102    public void setSimulatedLatLong(double lat, double lng) {
103        this.simulatedLatitude = lat;
104        this.simulatedLongitude = lng;
105    }
106
107    /**
108     * Sets a flag to indicate whether or not {@link #getFromLocationName(String, int)} should throw an exception to
109     * simulate a failure.
110     *
111     * @param shouldSimulateException whether or not an exception should be thrown from {@link #getFromLocationName(String, int)}
112     */
113    public void setShouldSimulateGeocodeException(boolean shouldSimulateException) {
114        this.shouldSimulateGeocodeException = true;
115    }
116
117    /**
118     * Non-Android accessor that indicates whether {@link #getFromLocation(double, double, int)} was called.
119     *
120     * @return whether {@link #getFromLocation(double, double, int)} was called.
121     */
122    public boolean wasGetFromLocationCalled() {
123        return wasCalled;
124    }
125
126    public double getLastLongitude() {
127        return lastLongitude;
128    }
129
130    public double getLastLatitude() {
131        return lastLatitude;
132    }
133
134    public String getLastLocationName() {
135        return lastLocationName;
136    }
137
138    private List<Address> oneElementList(Address address) {
139        ArrayList<Address> addresses = new ArrayList<Address>();
140        addresses.add(address);
141        return addresses;
142    }
143
144    public void setSimulatedHasLatLong(boolean hasLatitude, boolean hasLongitude) {
145        this.hasLatitude = hasLatitude;
146        this.hasLongitude = hasLongitude;
147    }
148
149    public void setReturnNoResults(boolean returnNoResults) {
150        this.returnNoResults = returnNoResults;
151    }
152
153    public boolean didResolution() {
154        return didResolution;
155    }
156}