Merge pull request #244 from misaochan/nearby-listview
Add list of nearby places without photos
|
|
@ -82,6 +82,10 @@
|
|||
<activity
|
||||
android:name=".auth.SignupActivity"
|
||||
android:label="@string/title_activity_signup"/>
|
||||
<activity
|
||||
android:name=".nearby.NearbyActivity"
|
||||
android:label="@string/title_activity_nearby"
|
||||
android:parentActivityName=".contributions.ContributionsActivity"/>
|
||||
|
||||
<service android:name=".upload.UploadService" >
|
||||
</service>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import fr.free.nrw.commons.AboutActivity;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.SettingsActivity;
|
||||
import fr.free.nrw.commons.nearby.NearbyActivity;
|
||||
import fr.free.nrw.commons.upload.UploadService;
|
||||
|
||||
public class ContributionsListFragment extends Fragment {
|
||||
|
|
@ -142,15 +143,28 @@ public class ContributionsListFragment extends Fragment {
|
|||
feedbackIntent.setType("message/rfc822");
|
||||
feedbackIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { CommonsApplication.FEEDBACK_EMAIL });
|
||||
feedbackIntent.putExtra(Intent.EXTRA_SUBJECT, String.format(CommonsApplication.FEEDBACK_EMAIL_SUBJECT, CommonsApplication.APPLICATION_VERSION));
|
||||
|
||||
try {
|
||||
startActivity(feedbackIntent);
|
||||
}
|
||||
catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(getActivity(), R.string.no_email_client, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return true;
|
||||
case R.id.menu_nearby:
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(this.getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
//See http://stackoverflow.com/questions/33169455/onrequestpermissionsresult-not-being-called-in-dialog-fragment
|
||||
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 2);
|
||||
return false;
|
||||
} else {
|
||||
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
|
||||
startActivity(nearbyIntent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
|
||||
startActivity(nearbyIntent);
|
||||
}
|
||||
case R.id.menu_refresh:
|
||||
((SourceRefresher)getActivity()).refreshSource();
|
||||
return true;
|
||||
|
|
@ -169,6 +183,14 @@ public class ContributionsListFragment extends Fragment {
|
|||
controller.startGalleryPick();
|
||||
}
|
||||
}
|
||||
// 2 = Location allowed when 'nearby places' selected
|
||||
case 2: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d("ContributionsList", "Location permission granted");
|
||||
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
|
||||
startActivity(nearbyIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
43
app/src/main/java/fr/free/nrw/commons/nearby/LatLng.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
public class LatLng {
|
||||
|
||||
public final double latitude;
|
||||
public final double longitude;
|
||||
|
||||
LatLng(double latitude, double longitude) {
|
||||
if(-180.0D <= longitude && longitude < 180.0D) {
|
||||
this.longitude = longitude;
|
||||
} else {
|
||||
this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D;
|
||||
}
|
||||
this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude));
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
boolean var1 = true;
|
||||
byte var2 = 1;
|
||||
long var3 = Double.doubleToLongBits(this.latitude);
|
||||
int var5 = 31 * var2 + (int)(var3 ^ var3 >>> 32);
|
||||
var3 = Double.doubleToLongBits(this.longitude);
|
||||
var5 = 31 * var5 + (int)(var3 ^ var3 >>> 32);
|
||||
return var5;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) {
|
||||
return true;
|
||||
} else if(!(o instanceof LatLng)) {
|
||||
return false;
|
||||
} else {
|
||||
LatLng var2 = (LatLng)o;
|
||||
return Double.doubleToLongBits(this.latitude) == Double.doubleToLongBits(var2.latitude) && Double.doubleToLongBits(this.longitude) == Double.doubleToLongBits(var2.longitude);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "lat/lng: (" + this.latitude + "," + this.longitude + ")";
|
||||
}
|
||||
}
|
||||
121
app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Criteria;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
public class NearbyActivity extends AppCompatActivity {
|
||||
|
||||
private MyLocationListener myLocationListener;
|
||||
private LocationManager locationManager;
|
||||
private String provider;
|
||||
private Criteria criteria;
|
||||
private LatLng mLatestLocation;
|
||||
|
||||
private double currentLatitude, currentLongitude;
|
||||
//private String gpsCoords;
|
||||
|
||||
private static final String TAG = "NearbyActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_nearby);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
registerLocationManager();
|
||||
|
||||
// Begin the transaction
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
NearbyListFragment fragment = new NearbyListFragment();
|
||||
ft.add(R.id.container, fragment);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume(){
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
protected LatLng getmLatestLocation() {
|
||||
return mLatestLocation;
|
||||
}
|
||||
/**
|
||||
* Registers a LocationManager to listen for current location
|
||||
*/
|
||||
protected void registerLocationManager() {
|
||||
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
criteria = new Criteria();
|
||||
provider = locationManager.getBestProvider(criteria, true);
|
||||
myLocationListener = new MyLocationListener();
|
||||
|
||||
try {
|
||||
locationManager.requestLocationUpdates(provider, 400, 1, myLocationListener);
|
||||
Location location = locationManager.getLastKnownLocation(provider);
|
||||
//Location works, just need to 'send' GPS coords via emulator extended controls if testing on emulator
|
||||
Log.d(TAG, "Checking for location...");
|
||||
if (location != null) {
|
||||
myLocationListener.onLocationChanged(location);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Illegal argument exception", e);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Security exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void unregisterLocationManager() {
|
||||
try {
|
||||
locationManager.removeUpdates(myLocationListener);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Security exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for user's location when it changes
|
||||
*/
|
||||
private class MyLocationListener implements LocationListener {
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
currentLatitude = location.getLatitude();
|
||||
currentLongitude = location.getLongitude();
|
||||
Log.d(TAG, "Latitude: " + String.valueOf(currentLatitude) + " Longitude: " + String.valueOf(currentLongitude));
|
||||
|
||||
mLatestLocation = new LatLng(currentLatitude, currentLongitude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
Log.d(TAG, provider + "'s status changed to " + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
Log.d(TAG, "Provider " + provider + " enabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
Log.d(TAG, "Provider " + provider + " disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy(){
|
||||
super.onDestroy();
|
||||
|
||||
unregisterLocationManager();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Adapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
public class NearbyListFragment extends ListFragment {
|
||||
|
||||
private int mImageSize;
|
||||
private boolean mItemClicked;
|
||||
private NearbyAdapter mAdapter;
|
||||
|
||||
private List<Place> places;
|
||||
private LatLng mLatestLocation;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
private static final String TAG = "NearbyListFragment";
|
||||
|
||||
public NearbyListFragment() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
Log.d(TAG, "NearbyListFragment created");
|
||||
View view = inflater.inflate(R.layout.fragment_nearby, container, false);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
|
||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
progressBar.setMax(10);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.setProgress(0);
|
||||
|
||||
mLatestLocation = ((NearbyActivity) getActivity()).getmLatestLocation();
|
||||
|
||||
getNearbyPlaces nearbyList = new getNearbyPlaces();
|
||||
nearbyList.execute();
|
||||
|
||||
Log.d(TAG, "Adapter set to ListView");
|
||||
|
||||
}
|
||||
|
||||
private List<Place> loadAttractionsFromLocation(final LatLng curLatLng) {
|
||||
|
||||
List<Place> places = NearbyPlaces.get();
|
||||
if (curLatLng != null) {
|
||||
Log.d(TAG, "Sorting places by distance...");
|
||||
Collections.sort(places,
|
||||
new Comparator<Place>() {
|
||||
@Override
|
||||
public int compare(Place lhs, Place rhs) {
|
||||
double lhsDistance = computeDistanceBetween(
|
||||
lhs.location, curLatLng);
|
||||
double rhsDistance = computeDistanceBetween(
|
||||
rhs.location, curLatLng);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 500; i++) {
|
||||
Place place = places.get(i);
|
||||
String distance = formatDistanceBetween(mLatestLocation, place.location);
|
||||
System.out.println("Sorted " + place.name + " at " + distance + " away.");
|
||||
place.setDistance(distance);
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
private class getNearbyPlaces extends AsyncTask<Void, Integer, List<Place>> {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
super.onProgressUpdate(values);
|
||||
progressBar.setProgress(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Place> doInBackground(Void... params) {
|
||||
places = loadAttractionsFromLocation(mLatestLocation);
|
||||
return places;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<Place> result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
mAdapter = new NearbyAdapter(getActivity(), places);
|
||||
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
||||
ListView listview = (ListView) getView().findViewById(R.id.listview);
|
||||
listview.setAdapter(mAdapter);
|
||||
|
||||
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
|
||||
|
||||
Place place = places.get(position);
|
||||
LatLng placeLatLng = place.location;
|
||||
|
||||
double latitude = placeLatLng.latitude;
|
||||
double longitude = placeLatLng.longitude;
|
||||
|
||||
Log.d(TAG, "Item at position " + position + " has coords: Lat: " + latitude + " Long: " + longitude);
|
||||
|
||||
//Open map app at given position
|
||||
Uri gmmIntentUri = Uri.parse("geo:0,0?q=" + latitude + "," + longitude);
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
|
||||
|
||||
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(mapIntent);
|
||||
}
|
||||
}
|
||||
});
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private class NearbyAdapter extends ArrayAdapter<Place> {
|
||||
|
||||
public List<Place> placesList;
|
||||
private Context mContext;
|
||||
|
||||
public NearbyAdapter(Context context, List<Place> places) {
|
||||
super(context, R.layout.item_place, places);
|
||||
mContext = context;
|
||||
placesList = places;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
// Get the data item for this position
|
||||
Place place = (Place) getItem(position);
|
||||
Log.d(TAG, "Place " + place.name);
|
||||
|
||||
// Check if an existing view is being reused, otherwise inflate the view
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_place, parent, false);
|
||||
}
|
||||
|
||||
// Lookup view for data population
|
||||
TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
|
||||
TextView tvDesc = (TextView) convertView.findViewById(R.id.tvDesc);
|
||||
TextView distance = (TextView) convertView.findViewById(R.id.distance);
|
||||
ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
|
||||
|
||||
String quotelessName = place.name.replaceAll("^\"|\"$", "");
|
||||
|
||||
// Populate the data into the template view using the data object
|
||||
tvName.setText(quotelessName);
|
||||
tvDesc.setText(place.description);
|
||||
distance.setText(place.distance);
|
||||
|
||||
//Types of desc: landmark, city, edu, event, mountain, isle
|
||||
switch(place.description) {
|
||||
case "landmark":
|
||||
icon.setImageResource(R.drawable.icon_landmark);
|
||||
break;
|
||||
case "city":
|
||||
icon.setImageResource(R.drawable.icon_city);
|
||||
break;
|
||||
case "edu":
|
||||
icon.setImageResource(R.drawable.icon_edu);
|
||||
break;
|
||||
case "event":
|
||||
icon.setImageResource(R.drawable.icon_event);
|
||||
break;
|
||||
case "mountain":
|
||||
icon.setImageResource(R.drawable.icon_mountain);
|
||||
break;
|
||||
case "isle":
|
||||
icon.setImageResource(R.drawable.icon_isle);
|
||||
break;
|
||||
default:
|
||||
icon.setImageResource(R.drawable.empty_photo);
|
||||
}
|
||||
|
||||
// Return the completed view to render on screen
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
private String formatDistanceBetween(LatLng point1, LatLng point2) {
|
||||
if (point1 == null || point2 == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NumberFormat numberFormat = NumberFormat.getNumberInstance();
|
||||
double distance = Math.round(computeDistanceBetween(point1, point2));
|
||||
|
||||
// Adjust to KM if M goes over 1000 (see javadoc of method for note
|
||||
// on only supporting metric)
|
||||
if (distance >= 1000) {
|
||||
numberFormat.setMaximumFractionDigits(1);
|
||||
return numberFormat.format(distance / 1000) + "km";
|
||||
}
|
||||
return numberFormat.format(distance) + "m";
|
||||
}
|
||||
|
||||
private static double computeDistanceBetween(LatLng from, LatLng to) {
|
||||
return computeAngleBetween(from, to) * 6371009.0D;
|
||||
}
|
||||
|
||||
private static double computeAngleBetween(LatLng from, LatLng to) {
|
||||
return distanceRadians(Math.toRadians(from.latitude), Math.toRadians(from.longitude), Math.toRadians(to.latitude), Math.toRadians(to.longitude));
|
||||
}
|
||||
|
||||
|
||||
private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
|
||||
return arcHav(havDistance(lat1, lat2, lng1 - lng2));
|
||||
}
|
||||
|
||||
private static double arcHav(double x) {
|
||||
return 2.0D * Math.asin(Math.sqrt(x));
|
||||
}
|
||||
|
||||
private static double havDistance(double lat1, double lat2, double dLng) {
|
||||
return hav(lat1 - lat2) + hav(dLng) * Math.cos(lat1) * Math.cos(lat2);
|
||||
}
|
||||
|
||||
private static double hav(double x) {
|
||||
double sinHalf = Math.sin(x * 0.5D);
|
||||
return sinHalf * sinHalf;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.StrictMode;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NearbyPlaces {
|
||||
|
||||
static List<Place> places = null;
|
||||
|
||||
public static List<Place> get() {
|
||||
if(places != null) {
|
||||
return places;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
places = new ArrayList<Place>();
|
||||
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
|
||||
URL file = new URL("https://tools.wmflabs.org/wiki-needs-pictures/data/data.csv");
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(file.openStream()));
|
||||
|
||||
boolean firstLine = true;
|
||||
String line;
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
line = in.readLine();
|
||||
|
||||
// Skip CSV header.
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
System.out.println(line);
|
||||
String[] fields = line.split(",");
|
||||
String name = fields[0];
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
try {
|
||||
latitude = Double.parseDouble(fields[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
latitude = 0;
|
||||
}
|
||||
try {
|
||||
longitude = Double.parseDouble(fields[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
longitude = 0;
|
||||
}
|
||||
|
||||
String type = fields[3];
|
||||
String image;
|
||||
|
||||
places.add(new Place(
|
||||
name,
|
||||
type, // list
|
||||
type, // details
|
||||
null,
|
||||
new LatLng(latitude, longitude)
|
||||
));
|
||||
}
|
||||
in.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
}
|
||||
33
app/src/main/java/fr/free/nrw/commons/nearby/Place.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
public class Place {
|
||||
|
||||
public String name;
|
||||
public String description;
|
||||
public String longDescription;
|
||||
public Uri secondaryImageUrl;
|
||||
public LatLng location;
|
||||
|
||||
public Bitmap image;
|
||||
public Bitmap secondaryImage;
|
||||
public String distance;
|
||||
|
||||
public Place() {}
|
||||
|
||||
public Place(String name, String description, String longDescription,
|
||||
Uri secondaryImageUrl, LatLng location) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.longDescription = longDescription;
|
||||
this.secondaryImageUrl = secondaryImageUrl;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public void setDistance(String distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -80,8 +80,8 @@ public class GPSExtractor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Extracts geolocation of image from EXIF data.
|
||||
* @return coordinates of image as string (needs to be passed as a String in API query)
|
||||
* Extracts geolocation (either of image from EXIF data, or of user)
|
||||
* @return coordinates as string (needs to be passed as a String in API query)
|
||||
*/
|
||||
@Nullable
|
||||
public String getCoords(boolean useGPS) {
|
||||
|
|
@ -103,6 +103,7 @@ public class GPSExtractor {
|
|||
return null;
|
||||
}
|
||||
|
||||
//If image has no EXIF data and user has enabled GPS setting, get user's location
|
||||
if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null && useGPS) {
|
||||
registerLocationManager();
|
||||
|
||||
|
|
@ -122,6 +123,7 @@ public class GPSExtractor {
|
|||
} else if (exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) {
|
||||
return null;
|
||||
} else {
|
||||
//If image has EXIF data, extract image coords
|
||||
imageCoordsExists = true;
|
||||
Log.d(TAG, "EXIF data has location info");
|
||||
|
||||
|
|
@ -142,6 +144,9 @@ public class GPSExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for user's location when it changes
|
||||
*/
|
||||
private class MyLocationListener implements LocationListener {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
BIN
app/src/main/res/drawable/empty_photo.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable/icon_city.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
app/src/main/res/drawable/icon_edu.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/drawable/icon_event.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
app/src/main/res/drawable/icon_isle.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
app/src/main/res/drawable/icon_landmark.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
app/src/main/res/drawable/icon_mountain.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/drawable/icon_war.jpg
Normal file
|
After Width: | Height: | Size: 203 KiB |
13
app/src/main/res/layout/activity_campaigns.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/campaignsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
</FrameLayout>
|
||||
13
app/src/main/res/layout/activity_nearby.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
18
app/src/main/res/layout/fragment_campaigns.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<GridView android:id="@+id/campaignsList"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:stretchMode="columnWidth"
|
||||
android:columnWidth="240dp"
|
||||
android:numColumns="auto_fit"
|
||||
android:listSelector="@null"
|
||||
android:fadingEdge="none"
|
||||
android:fastScrollEnabled="false"
|
||||
/>
|
||||
</LinearLayout>
|
||||
18
app/src/main/res/layout/fragment_nearby.xml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<ListView
|
||||
android:id="@+id/listview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
</ListView>
|
||||
|
||||
</RelativeLayout>
|
||||
59
app/src/main/res/layout/item_place.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:layout_height="@dimen/icon_size">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/empty_photo"
|
||||
android:background="#ffffff"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/distance"
|
||||
android:layout_width="@dimen/icon_size"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/icon"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/tiny_margin"
|
||||
style="?android:textAppearanceSmallInverse"
|
||||
android:textColor="#ffffff"
|
||||
android:background="@color/text_background"
|
||||
tools:text="Overlay"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_toRightOf="@+id/icon"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingLeft="@dimen/small_margin"
|
||||
android:paddingRight="@dimen/small_margin"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="none"
|
||||
style="?android:textAppearanceMedium"
|
||||
tools:text="Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvDesc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@+id/icon"
|
||||
android:layout_toRightOf="@+id/icon"
|
||||
android:layout_below="@+id/tvName"
|
||||
android:padding="@dimen/small_margin"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="4"
|
||||
style="?android:textAppearanceSmall"
|
||||
tools:text="Description" />
|
||||
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
12
app/src/main/res/layout/layout_campaign_item.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/campaignItemName"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
|
@ -27,6 +27,11 @@
|
|||
app:showAsAction="never"
|
||||
android:icon="@android:drawable/ic_menu_send"
|
||||
/>
|
||||
<item android:id="@+id/menu_nearby"
|
||||
android:title="@string/menu_nearby"
|
||||
app:showAsAction="never"
|
||||
android:icon="@android:drawable/ic_menu_myplaces"
|
||||
/>
|
||||
<item android:id="@+id/menu_refresh"
|
||||
android:title="@string/menu_refresh"
|
||||
app:showAsAction="never"
|
||||
|
|
|
|||
8
app/src/main/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<color name="text_background">#90000000</color>
|
||||
<color name="transparent_actionbar_background">#22000000</color>
|
||||
<color name="lighter_gray">#ddd</color>
|
||||
|
||||
</resources>
|
||||
|
|
@ -2,4 +2,7 @@
|
|||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="icon_size">120dp</dimen>
|
||||
<dimen name="tiny_margin">4dp</dimen>
|
||||
<dimen name="small_margin">8dp</dimen>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
<string name="contribution_state_starting">Uploading</string>
|
||||
<string name="menu_from_gallery">From Gallery</string>
|
||||
<string name="menu_from_camera">Take photo</string>
|
||||
<string name="menu_nearby">Nearby</string>
|
||||
|
||||
<string name="provider_contributions">My uploads</string>
|
||||
<string name="menu_share">Share</string>
|
||||
|
|
@ -154,4 +155,6 @@
|
|||
<string name="location_permission_rationale">Optional permission: Get current location for category suggestions</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="back">Back</string>
|
||||
|
||||
<string name="title_activity_nearby">Nearby Places</string>
|
||||
</resources>
|
||||
|
|
|
|||
9
app/src/main/res/xml/campaigns_sync_adapter.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:contentAuthority="fr.free.nrw.commons.campaigns.contentprovider"
|
||||
android:accountType="fr.free.nrw.commons"
|
||||
android:supportsUploading="false"
|
||||
android:userVisible="true"
|
||||
android:isAlwaysSyncable="true"
|
||||
/>
|
||||
51
disable-campaigns.patch
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
|
||||
index 5ad1c77..50b16ff 100644
|
||||
--- a/app/src/main/AndroidManifest.xml
|
||||
+++ b/app/src/main/AndroidManifest.xml
|
||||
@@ -82,14 +82,6 @@
|
||||
android:label="@string/title_activity_settings"
|
||||
/>
|
||||
<activity android:name=".AboutActivity" android:label="@string/title_activity_about"/>
|
||||
- <activity android:name=".campaigns.CampaignActivity" android:label="Campaigns"
|
||||
- android:icon="@drawable/ic_launcher"
|
||||
- >
|
||||
- <intent-filter>
|
||||
- <action android:name="android.intent.action.MAIN"/>
|
||||
- <category android:name="android.intent.category.LAUNCHER"/>
|
||||
- </intent-filter>
|
||||
- </activity>
|
||||
|
||||
<service android:name=".upload.UploadService" >
|
||||
</service>
|
||||
@@ -106,17 +98,6 @@
|
||||
android:resource="@xml/authenticator" />
|
||||
</service>
|
||||
<service
|
||||
- android:name=".campaigns.CampaignsSyncService"
|
||||
- android:exported="true">
|
||||
- <intent-filter>
|
||||
- <action
|
||||
- android:name="android.content.SyncAdapter" />
|
||||
- </intent-filter>
|
||||
- <meta-data
|
||||
- android:name="android.content.SyncAdapter"
|
||||
- android:resource="@xml/campaigns_sync_adapter" />
|
||||
- </service>
|
||||
- <service
|
||||
android:name=".contributions.ContributionsSyncService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
@@ -148,13 +129,6 @@
|
||||
android:exported="false">
|
||||
</provider>
|
||||
<provider
|
||||
- android:name=".campaigns.CampaignsContentProvider"
|
||||
- android:label="@string/provider_campaigns"
|
||||
- android:syncable="true"
|
||||
- android:authorities="fr.free.nrw.commons.campaigns.contentprovider"
|
||||
- android:exported="false">
|
||||
- </provider>
|
||||
- <provider
|
||||
android:name=".modifications.ModificationsContentProvider"
|
||||
android:label="@string/provider_modifications"
|
||||
android:syncable="true"
|
||||