NetworkContentProvider: Remove downloading and usage of predefined station databases.

This commit is contained in:
Andreas Schildbach 2020-09-17 13:47:18 +02:00
parent f17bf1f776
commit 6250043844
7 changed files with 42 additions and 553 deletions

View file

@ -228,11 +228,6 @@
android:label="@string/stations_favorite_stations_title" android:label="@string/stations_favorite_stations_title"
android:taskAffinity="de.schildbach.oeffi.stations" /> android:taskAffinity="de.schildbach.oeffi.stations" />
<provider
android:name=".stations.NetworkContentProvider"
android:authorities="de.schildbach.oeffi.networks"
android:exported="false" />
<provider <provider
android:name=".stations.FavoriteStationsProvider" android:name=".stations.FavoriteStationsProvider"
android:authorities="de.schildbach.oeffi.stations.favorites" android:authorities="de.schildbach.oeffi.stations.favorites"

View file

@ -26,7 +26,6 @@ import okhttp3.HttpUrl;
public class Constants { public class Constants {
public static final HttpUrl OEFFI_BASE_URL = HttpUrl.parse("https://oeffi.schildbach.de/"); public static final HttpUrl OEFFI_BASE_URL = HttpUrl.parse("https://oeffi.schildbach.de/");
public static final HttpUrl STATIONS_BASE_URL = OEFFI_BASE_URL.newBuilder().addPathSegment("stations").build();
public static final HttpUrl PLANS_BASE_URL = OEFFI_BASE_URL.newBuilder().addPathSegment("plans").build(); public static final HttpUrl PLANS_BASE_URL = OEFFI_BASE_URL.newBuilder().addPathSegment("plans").build();
public static final HttpUrl MESSAGES_BASE_URL = OEFFI_BASE_URL.newBuilder().addPathSegment("messages").build(); public static final HttpUrl MESSAGES_BASE_URL = OEFFI_BASE_URL.newBuilder().addPathSegment("messages").build();
public static final String PLANS_DIR = "plans"; public static final String PLANS_DIR = "plans";

View file

@ -42,7 +42,6 @@ import de.schildbach.oeffi.R;
import de.schildbach.oeffi.TripAware; import de.schildbach.oeffi.TripAware;
import de.schildbach.oeffi.directions.TimeSpec.DepArr; import de.schildbach.oeffi.directions.TimeSpec.DepArr;
import de.schildbach.oeffi.stations.LineView; import de.schildbach.oeffi.stations.LineView;
import de.schildbach.oeffi.stations.NetworkContentProvider;
import de.schildbach.oeffi.stations.StationContextMenu; import de.schildbach.oeffi.stations.StationContextMenu;
import de.schildbach.oeffi.stations.StationDetailsActivity; import de.schildbach.oeffi.stations.StationDetailsActivity;
import de.schildbach.oeffi.util.Formats; import de.schildbach.oeffi.util.Formats;
@ -54,7 +53,6 @@ import de.schildbach.pte.NetworkId;
import de.schildbach.pte.dto.Fare; import de.schildbach.pte.dto.Fare;
import de.schildbach.pte.dto.Line; import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Point;
import de.schildbach.pte.dto.Position; import de.schildbach.pte.dto.Position;
import de.schildbach.pte.dto.Stop; import de.schildbach.pte.dto.Stop;
@ -72,7 +70,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Typeface; import android.graphics.Typeface;
@ -695,13 +692,6 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen
if (leg.arrival.hasCoord()) { if (leg.arrival.hasCoord()) {
mapView.setVisibility(View.VISIBLE); mapView.setVisibility(View.VISIBLE);
mapView.setOnClickListener(new MapClickListener(leg.arrival)); mapView.setOnClickListener(new MapClickListener(leg.arrival));
} else if (leg.arrival.hasId()) {
final Point point = pointFromStationDb(leg.arrival.id);
if (point != null) {
mapView.setVisibility(View.VISIBLE);
mapView.setOnClickListener(new MapClickListener(new Location(LocationType.STATION, leg.arrival.id,
point, leg.arrival.place, leg.arrival.name)));
}
} }
} }
@ -940,53 +930,6 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen
} }
} }
private Point pointFromStationDb(final String id) {
final Cursor cursor = getContentResolver().query(
NetworkContentProvider.CONTENT_URI.buildUpon().appendPath(network.name()).build(),
new String[] { NetworkContentProvider.KEY_LAT, NetworkContentProvider.KEY_LON },
NetworkContentProvider.KEY_ID + "=?", new String[] { id }, null);
if (cursor == null)
return null;
final Point point;
if (cursor.moveToFirst()) {
final int latColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
point = Point.from1E6(cursor.getInt(latColumnIndex), cursor.getInt(lonColumnIndex));
} else {
point = null;
}
cursor.close();
return point;
}
private Point pointFromStationDb(final String place, final String name) {
final Cursor cursor = getContentResolver().query(
NetworkContentProvider.CONTENT_URI.buildUpon().appendPath(network.name())
.appendQueryParameter(NetworkContentProvider.KEY_PLACE, place != null ? place : "")
.appendQueryParameter(NetworkContentProvider.KEY_NAME, name != null ? name : "").build(),
new String[] { NetworkContentProvider.KEY_LAT, NetworkContentProvider.KEY_LON }, null, null, null);
if (cursor == null)
return null;
final Point point;
if (cursor.moveToFirst()) {
final int latColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
point = Point.from1E6(cursor.getInt(latColumnIndex), cursor.getInt(lonColumnIndex));
} else {
point = null;
}
cursor.close();
return point;
}
private void shareTripShort() { private void shareTripShort() {
final Intent intent = new Intent(Intent.ACTION_SEND); final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain"); intent.setType("text/plain");
@ -1113,19 +1056,6 @@ public class TripDetailsActivity extends OeffiActivity implements LocationListen
if (location.hasCoord()) if (location.hasCoord())
return location.coord; return location.coord;
if (location.hasId()) {
final Point point = pointFromStationDb(location.id);
if (point != null)
return point;
}
// fallback kludge: search db by name
if (location.name != null) {
final Point point = pointFromStationDb(location.place, location.name);
if (point != null)
return point;
}
return null; return null;
} }

View file

@ -234,25 +234,6 @@ public class NearestFavoriteStationWidgetService extends JobIntentService {
final NetworkId networkId = NetworkId.valueOf(network); final NetworkId networkId = NetworkId.valueOf(network);
NetworkProviderFactory.provider(networkId); // check if existent NetworkProviderFactory.provider(networkId); // check if existent
final Cursor stationCursor = contentResolver.query(
NetworkContentProvider.CONTENT_URI.buildUpon().appendPath(networkId.name()).build(), null,
NetworkContentProvider.KEY_ID + "=?", new String[] { stationId }, null);
if (stationCursor != null) {
if (stationCursor.moveToFirst()) {
final int placeCol = stationCursor.getColumnIndex(NetworkContentProvider.KEY_PLACE);
final int nameCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_NAME);
final int latCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
if (placeCol != -1)
stationPlace = stationCursor.getString(placeCol);
stationName = stationCursor.getString(nameCol);
stationPoint = Point.from1E6(stationCursor.getInt(latCol), stationCursor.getInt(lonCol));
}
stationCursor.close();
}
if (stationPoint.getLatAsDouble() > 0 || stationPoint.getLonAsDouble() > 0) { if (stationPoint.getLatAsDouble() > 0 || stationPoint.getLonAsDouble() > 0) {
final float[] distanceBetweenResults = new float[1]; final float[] distanceBetweenResults = new float[1];
android.location.Location.distanceBetween(here.getLatitude(), here.getLongitude(), android.location.Location.distanceBetween(here.getLatitude(), here.getLongitude(),

View file

@ -17,208 +17,12 @@
package de.schildbach.oeffi.stations; package de.schildbach.oeffi.stations;
import java.io.File;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import de.schildbach.oeffi.Application;
import de.schildbach.oeffi.Constants;
import de.schildbach.oeffi.util.Downloader;
import de.schildbach.pte.NetworkId; import de.schildbach.pte.NetworkId;
import android.content.ContentProvider; import java.util.Locale;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import androidx.annotation.Nullable;
import okhttp3.HttpUrl;
public final class NetworkContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse("content://de.schildbach.oeffi.networks");
private static final String DATABASE_TABLE = "stations";
public static final String KEY_ID = "_id";
public static final String KEY_LOCAL_ID = "local_id"; // optional!
public static final String KEY_PLACE = "place"; // optional!
public static final String KEY_NAME = "name";
public static final String KEY_LAT = "lat";
public static final String KEY_LON = "lon";
public static final String KEY_PRODUCTS = "products"; // optional!
public static final String KEY_LINES = "lines";
public static final String QUERY_PARAM_Q = "q";
private Application application;
private Downloader downloader;
private final List<SQLiteDatabase> databasesToClose = new LinkedList<>();
private static final int NUM_DATABASES_TO_KEEP = 4;
private static Pattern PATTERN_Q_ID = Pattern.compile("\\d+");
public class NetworkContentProvider {
public static String dbName(final NetworkId networkId) { public static String dbName(final NetworkId networkId) {
return networkId.name().toLowerCase(Locale.ENGLISH) + ".db"; return networkId.name().toLowerCase(Locale.ENGLISH) + ".db";
} }
private static HttpUrl downloadUrl(final NetworkId networkId) {
return Constants.STATIONS_BASE_URL.newBuilder()
.addPathSegment(networkId.name().toLowerCase(Locale.ENGLISH) + ".db.bz2").build();
}
private static final Logger log = LoggerFactory.getLogger(NetworkContentProvider.class);
@Override
public boolean onCreate() {
this.application = (Application) getContext();
downloader = new Downloader(application.getCacheDir());
return true;
}
@Override
public synchronized void shutdown() {
for (final Iterator<SQLiteDatabase> i = databasesToClose.iterator(); i.hasNext();) {
i.next().close();
i.remove();
}
}
@Override
public synchronized Cursor query(final Uri uri, final String[] projection, final String _selection,
final String[] _selectionArgs, final String sortOrder) {
if (databasesToClose.size() >= NUM_DATABASES_TO_KEEP)
databasesToClose.remove(0).close();
final NetworkId networkId = NetworkId.valueOf(uri.getPathSegments().get(0));
final File dbFile = new File(getContext().getFilesDir(), dbName(networkId));
final HttpUrl remoteUrl = downloadUrl(networkId);
final ListenableFuture<Integer> download = downloader.download(application.okHttpClient(), remoteUrl, dbFile, true, null);
Futures.addCallback(download, new FutureCallback<Integer>() {
public void onSuccess(final @Nullable Integer status) {
if (status == HttpURLConnection.HTTP_OK)
getContext().getContentResolver().notifyChange(uri, null);
}
public void onFailure(final Throwable t) {
}
}, MoreExecutors.directExecutor());
if (!dbFile.exists())
return null;
final String lat = uri.getQueryParameter("lat");
final String lon = uri.getQueryParameter("lon");
final String ids = uri.getQueryParameter("ids");
final String place = uri.getQueryParameter(KEY_PLACE);
final String name = uri.getQueryParameter(KEY_NAME);
final String q = uri.getQueryParameter(QUERY_PARAM_Q);
final String devLat = "100000"; // 0.1 degrees
final String devLon = "200000"; // 0.2 degrees
final SQLiteDatabase db = SQLiteDatabase.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
// test if database contains optional columns
final Cursor testCursor = db.query(DATABASE_TABLE, null, null, null, null, null, null, "0");
final boolean hasLocalId = testCursor.getColumnIndex(NetworkContentProvider.KEY_LOCAL_ID) != -1;
final boolean hasPlace = testCursor.getColumnIndex(NetworkContentProvider.KEY_PLACE) != -1;
final boolean hasProducts = testCursor.getColumnIndex(NetworkContentProvider.KEY_PRODUCTS) != -1;
testCursor.close();
String selection = null;
List<String> selectionArgs = new ArrayList<>();
if (lat != null || lon != null) {
selection = "(" + KEY_LAT + ">?-" + devLat + " AND " + KEY_LAT + "<?+" + devLat + " AND " //
+ KEY_LON + ">?-" + devLon + " AND " + KEY_LON + "<?+" + devLon + ")";
selectionArgs.addAll(Arrays.asList(lat, lat, lon, lon));
}
if (place != null && hasPlace) {
selection = (selection != null ? selection + " AND " : "") + KEY_PLACE
+ (!place.isEmpty() ? "=?" : " IS NULL");
if (!place.isEmpty())
selectionArgs.add(place);
}
if (name != null) {
selection = (selection != null ? selection + " AND " : "") + KEY_NAME
+ (!name.isEmpty() ? "=?" : " IS NULL");
if (!name.isEmpty())
selectionArgs.add(name);
}
if (ids != null && ids.length() > 0 && selection != null) {
final StringBuilder escapedIds = new StringBuilder();
for (final String id : ids.split(",")) {
DatabaseUtils.appendEscapedSQLString(escapedIds, id);
escapedIds.append(',');
}
if (escapedIds.length() > 0)
escapedIds.setLength(escapedIds.length() - 1);
selection = "(" + selection + ") OR " + (hasLocalId ? KEY_LOCAL_ID : KEY_ID) + " IN (" + escapedIds + ")";
}
if (q != null) {
final boolean maybeId = PATTERN_Q_ID.matcher(q).matches();
selection = (selection != null ? "(" + selection + ") AND " : "") + "("
+ (maybeId ? KEY_ID + " = ? OR " : "") + (hasPlace ? KEY_PLACE + " LIKE ? OR " : "") + KEY_NAME
+ " LIKE ?)";
if (maybeId)
selectionArgs.add(q);
if (hasPlace)
selectionArgs.add("%" + q + "%");
selectionArgs.add("%" + q + "%");
}
if (_selection != null) {
selection = (selection != null ? "(" + selection + ") AND " : "") //
+ "(" + _selection + ")";
if (_selectionArgs != null)
selectionArgs.addAll(Arrays.asList(_selectionArgs));
}
final Cursor result = db.query(DATABASE_TABLE, projection, selection, selectionArgs.toArray(new String[0]),
null, null, sortOrder);
result.setNotificationUri(getContext().getContentResolver(), uri);
databasesToClose.add(db);
return result;
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException();
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
} }

View file

@ -28,7 +28,6 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -37,7 +36,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import de.schildbach.oeffi.Constants; import de.schildbach.oeffi.Constants;
import de.schildbach.oeffi.MyActionBar; import de.schildbach.oeffi.MyActionBar;
@ -57,11 +55,8 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.LineDestination; import de.schildbach.pte.dto.LineDestination;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType; import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.Point;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryDeparturesResult; import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.StationDepartures; import de.schildbach.pte.dto.StationDepartures;
import de.schildbach.pte.dto.Style;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
@ -69,7 +64,6 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -122,8 +116,6 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
private Integer selectedFavState = null; private Integer selectedFavState = null;
@Nullable @Nullable
private LinkedHashMap<Line, List<Location>> selectedLines = null; private LinkedHashMap<Line, List<Location>> selectedLines = null;
@Nullable
private List<Line> selectedAdditionalLines = null;
private MyActionBar actionBar; private MyActionBar actionBar;
private ToggleImageButton favoriteButton; private ToggleImageButton favoriteButton;
@ -381,68 +373,6 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
for (final StationDepartures stationDepartures : result.stationDepartures) { for (final StationDepartures stationDepartures : result.stationDepartures) {
Location location = stationDepartures.location; Location location = stationDepartures.location;
if (location.hasId()) { if (location.hasId()) {
List<Line> additionalLines = null;
final Cursor cursor = getContentResolver().query(
NetworkContentProvider.CONTENT_URI.buildUpon()
.appendPath(selectedNetwork.name()).build(),
null, NetworkContentProvider.KEY_ID + "=?", new String[] { location.id },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
final int placeCol = cursor
.getColumnIndex(NetworkContentProvider.KEY_PLACE);
final int nameCol = cursor
.getColumnIndexOrThrow(NetworkContentProvider.KEY_NAME);
final int latCol = cursor
.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonCol = cursor
.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
final int productsCol = cursor
.getColumnIndex(NetworkContentProvider.KEY_PRODUCTS);
final int linesCol = cursor
.getColumnIndexOrThrow(NetworkContentProvider.KEY_LINES);
final Point coord = Point.from1E6(cursor.getInt(latCol),
cursor.getInt(lonCol));
final String place = placeCol != -1 ? cursor.getString(placeCol)
: selectedStation.place;
final String name = cursor.getString(nameCol);
final Set<Product> products;
if (productsCol != -1 && !cursor.isNull(productsCol))
products = Product
.fromCodes(cursor.getString(productsCol).toCharArray());
else
products = null;
location = new Location(LocationType.STATION, location.id, coord, place,
name, products);
final String[] additionalLinesArray = cursor.getString(linesCol).split(",");
additionalLines = new ArrayList<>(additionalLinesArray.length);
l: for (final String additionalLineStr : additionalLinesArray) {
if (!additionalLineStr.isEmpty()) {
final Product additionalLineProduct = Product
.fromCode(additionalLineStr.charAt(0));
final String additionalLineLabel = Strings
.emptyToNull(additionalLineStr.substring(1));
final Line additionalLine = new Line(null, null,
additionalLineProduct, additionalLineLabel);
final List<LineDestination> lineDestinations = stationDepartures.lines;
if (lineDestinations != null)
for (final LineDestination lineDestination : lineDestinations)
if (lineDestination.line.equals(additionalLine))
continue l;
final Style style = networkProvider.lineStyle(null,
additionalLine.product, additionalLine.label);
additionalLines.add(new Line(null, null, additionalLine.product,
additionalLine.label, style));
}
}
}
cursor.close();
}
Station station = findStation(location.id); Station station = findStation(location.id);
if (station == null) { if (station == null) {
station = new Station(selectedNetwork, location); station = new Station(selectedNetwork, location);
@ -455,7 +385,6 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
if (location.equals(selectedStation)) { if (location.equals(selectedStation)) {
selectedDepartures = stationDepartures.departures; selectedDepartures = stationDepartures.departures;
selectedLines = groupDestinationsByLine(stationDepartures.lines); selectedLines = groupDestinationsByLine(stationDepartures.lines);
selectedAdditionalLines = additionalLines;
} }
} }
} }
@ -510,53 +439,6 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
selectedStation = station.location; selectedStation = station.location;
selectedDepartures = station.departures; selectedDepartures = station.departures;
selectedLines = groupDestinationsByLine(station.getLines()); selectedLines = groupDestinationsByLine(station.getLines());
selectedAdditionalLines = null;
final Cursor stationCursor = getContentResolver().query(
NetworkContentProvider.CONTENT_URI.buildUpon().appendPath(selectedNetwork.name()).build(), null,
NetworkContentProvider.KEY_ID + "=?", new String[] { selectedStation.id }, null);
if (stationCursor != null) {
if (stationCursor.moveToFirst()) {
final int placeCol = stationCursor.getColumnIndex(NetworkContentProvider.KEY_PLACE);
final int nameCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_NAME);
final int latCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
final int productsCol = stationCursor.getColumnIndex(NetworkContentProvider.KEY_PRODUCTS);
final int linesCol = stationCursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LINES);
final Point coord = Point.from1E6(stationCursor.getInt(latCol), stationCursor.getInt(lonCol));
final String place = placeCol != -1 ? stationCursor.getString(placeCol) : selectedStation.place;
final String name = stationCursor.getString(nameCol);
final Set<Product> products;
if (productsCol != -1 && !stationCursor.isNull(productsCol))
products = Product.fromCodes(stationCursor.getString(productsCol).toCharArray());
else
products = null;
selectedStation = new Location(LocationType.STATION, selectedStation.id, coord, place, name, products);
final NetworkProvider networkProvider = NetworkProviderFactory.provider(selectedNetwork);
final String[] additionalLinesArray = stationCursor.getString(linesCol).split(",");
final List<Line> additionalLines = new ArrayList<>(additionalLinesArray.length);
l: for (final String additionalLineStr : additionalLinesArray) {
if (!additionalLineStr.isEmpty()) {
final Product additionalLineProduct = Product.fromCode(additionalLineStr.charAt(0));
final String additionalLineLabel = Strings.emptyToNull(additionalLineStr.substring(1));
final Line additionalLine = new Line(null, null, additionalLineProduct, additionalLineLabel);
final List<LineDestination> lineDestinations = station.getLines();
if (lineDestinations != null)
for (final LineDestination line : lineDestinations)
if (line.line.equals(additionalLine))
continue l;
additionalLines.add(new Line(null, null, additionalLine.product, additionalLine.label,
networkProvider.lineStyle(null, additionalLine.product, additionalLine.label)));
}
}
selectedAdditionalLines = additionalLines;
}
stationCursor.close();
}
selectedFavState = FavoriteStationsProvider.favState(getContentResolver(), selectedNetwork, selectedStation); selectedFavState = FavoriteStationsProvider.favState(getContentResolver(), selectedNetwork, selectedStation);
@ -647,7 +529,7 @@ public class StationDetailsActivity extends OeffiActivity implements StationsAwa
@Override @Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) { if (holder instanceof HeaderViewHolder) {
((HeaderViewHolder) holder).bind(selectedStation, selectedLines, selectedAdditionalLines); ((HeaderViewHolder) holder).bind(selectedStation, selectedLines, null);
} else { } else {
final Departure departure = getItem(position); final Departure departure = getItem(position);
((DepartureViewHolder) holder).bind(selectedNetwork, departure); ((DepartureViewHolder) holder).bind(selectedNetwork, departure);

View file

@ -33,15 +33,12 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.osmdroid.util.BoundingBox; import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint; import org.osmdroid.util.GeoPoint;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
@ -72,11 +69,11 @@ import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.LineDestination; import de.schildbach.pte.dto.LineDestination;
import de.schildbach.pte.dto.Location; import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType; import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Point; import de.schildbach.pte.dto.Point;
import de.schildbach.pte.dto.Product; import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryDeparturesResult; import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.StationDepartures; import de.schildbach.pte.dto.StationDepartures;
import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.SuggestLocationsResult; import de.schildbach.pte.dto.SuggestLocationsResult;
import android.Manifest; import android.Manifest;
@ -90,8 +87,6 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -105,7 +100,6 @@ import android.location.LocationManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.Uri; import android.net.Uri;
import android.net.Uri.Builder;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -449,9 +443,6 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware
if (network != null && NetworkProviderFactory.provider(network).hasCapabilities(Capability.DEPARTURES)) { if (network != null && NetworkProviderFactory.provider(network).hasCapabilities(Capability.DEPARTURES)) {
startLocationProvider(); startLocationProvider();
// request update on content change (db loaded)
getContentResolver().registerContentObserver(NetworkContentProvider.CONTENT_URI, true, contentObserver);
// request update on orientation change // request update on orientation change
sensorManager.registerListener(orientationListener, sensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); sensorManager.registerListener(orientationListener, sensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
sensorManager.registerListener(orientationListener, sensorMagnetometer, SensorManager.SENSOR_DELAY_NORMAL); sensorManager.registerListener(orientationListener, sensorMagnetometer, SensorManager.SENSOR_DELAY_NORMAL);
@ -529,9 +520,6 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware
// cancel update on orientation change // cancel update on orientation change
sensorManager.unregisterListener(orientationListener); sensorManager.unregisterListener(orientationListener);
// cancel content change
getContentResolver().unregisterContentObserver(contentObserver);
// cancel background thread // cancel background thread
backgroundThread.getLooper().quit(); backgroundThread.getLooper().quit();
@ -755,13 +743,6 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware
return super.onCreateDialog(id); return super.onCreateDialog(id);
} }
private final ContentObserver contentObserver = new ContentObserver(handler) {
@Override
public void onChange(final boolean selfChange) {
runOnUiThread(initStationsRunnable);
}
};
private final Runnable initStationsRunnable = new Runnable() { private final Runnable initStationsRunnable = new Runnable() {
public void run() { public void run() {
if (network != null) { if (network != null) {
@ -792,75 +773,42 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware
updateGUI(); updateGUI();
}); });
final Builder uriBuilder = NetworkContentProvider.CONTENT_URI.buildUpon(); final NetworkProvider networkProvider = NetworkProviderFactory.provider(network);
uriBuilder.appendPath(network.name()); try {
uriBuilder.appendQueryParameter("lat", Integer.toString(referenceLocation.getLatAs1E6())); final NearbyLocationsResult result =
uriBuilder.appendQueryParameter("lon", Integer.toString(referenceLocation.getLonAs1E6())); networkProvider.queryNearbyLocations(EnumSet.of(LocationType.STATION),
uriBuilder.appendQueryParameter("ids", favoriteIds.toString()); referenceLocation, 0, 0);
final Cursor cursor = getContentResolver().query(uriBuilder.build(), null, null, null, null); if (result.status == NearbyLocationsResult.Status.OK) {
log.info("Got {}", result.toShortString());
if (cursor != null) { final List<Station> freshStations = new ArrayList<>(result.locations.size());
final int nativeIdColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_ID); final float[] distanceBetweenResults = new float[2];
final int localIdColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_LOCAL_ID);
final int placeColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_PLACE);
final int nameColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_NAME);
final int latColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT);
final int lonColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON);
final int productsColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_PRODUCTS);
final int linesColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LINES);
final List<Station> freshStations = new ArrayList<>(cursor.getCount()); for (final Location location : result.locations) {
if (location.type == LocationType.STATION) {
final float[] distanceBetweenResults = new float[2]; final Station station = new Station(network, location, null);
if (deviceLocation != null) {
final NetworkProvider networkProvider = NetworkProviderFactory.provider(network); android.location.Location.distanceBetween(referenceLocation.getLatAsDouble(),
referenceLocation.getLonAsDouble(), location.getLatAsDouble(),
while (cursor.moveToNext()) { location.getLonAsDouble(), distanceBetweenResults);
final List<LineDestination> lineDestinations = new LinkedList<>(); station.setDistanceAndBearing(distanceBetweenResults[0],
for (final String lineStr : cursor.getString(linesColumnIndex).split(",")) { distanceBetweenResults[1]);
if (!lineStr.isEmpty()) { }
final Product product = Product.fromCode(lineStr.charAt(0)); freshStations.add(station);
final String label = Strings.emptyToNull(lineStr.substring(1));
// FIXME don't access networkProvider
// from thread
final Style style = networkProvider.lineStyle(null, product, label);
lineDestinations.add(
new LineDestination(new Line(null, null, product, label, style), null));
} }
} }
final String id = localIdColumnIndex != -1 ? cursor.getString(localIdColumnIndex) runOnUiThread(() -> mergeIntoStations(freshStations, true));
: cursor.getString(nativeIdColumnIndex);
final String place = placeColumnIndex != -1 ? cursor.getString(placeColumnIndex) : null;
final String name = cursor.getString(nameColumnIndex);
final Point coord = Point.from1E6(cursor.getInt(latColumnIndex),
cursor.getInt(lonColumnIndex));
final Set<Product> products;
if (productsColumnIndex != -1 && !cursor.isNull(productsColumnIndex))
products = Product.fromCodes(cursor.getString(productsColumnIndex).toCharArray());
else
products = null;
final Station station = new Station(network, new Location(
LocationType.STATION, id, coord, place, name, products), lineDestinations);
if (deviceLocation != null) {
android.location.Location.distanceBetween(referenceLocation.getLatAsDouble(),
referenceLocation.getLonAsDouble(), coord.getLatAsDouble(), coord.getLonAsDouble(),
distanceBetweenResults);
station.setDistanceAndBearing(distanceBetweenResults[0], distanceBetweenResults[1]);
}
freshStations.add(station);
} }
cursor.close(); runOnUiThread(() -> {
actionBar.stopProgress();
runOnUiThread(() -> mergeIntoStations(freshStations, true)); loading = false;
updateGUI();
});
} catch (final IOException x) {
log.info("IO problem while querying for nearby locations to " + referenceLocation, x);
} }
runOnUiThread(() -> {
actionBar.stopProgress();
loading = false;
updateGUI();
});
}); });
} }
@ -1524,69 +1472,19 @@ public class StationsActivity extends OeffiMainActivity implements StationsAware
final String query = params[0]; final String query = params[0];
final Builder uriBuilder = NetworkContentProvider.CONTENT_URI.buildUpon();
uriBuilder.appendPath(network.name());
uriBuilder.appendQueryParameter(NetworkContentProvider.QUERY_PARAM_Q, query);
final Cursor cursor = getContentResolver().query(uriBuilder.build(), null, null, null,
NetworkContentProvider.KEY_NAME);
final Matcher mQuery = Pattern.compile("(^|[ -/\\(])" + query, Pattern.CASE_INSENSITIVE).matcher("");
final NetworkProvider networkProvider = NetworkProviderFactory.provider(network); final NetworkProvider networkProvider = NetworkProviderFactory.provider(network);
final List<Station> stations = new LinkedList<>(); final List<Station> stations = new LinkedList<>();
if (cursor != null) { try {
final int nativeIdColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_ID); final SuggestLocationsResult result = networkProvider.suggestLocations(query,
final int localIdColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_LOCAL_ID); EnumSet.of(LocationType.STATION), 0);
final int placeColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_PLACE); if (result.status == SuggestLocationsResult.Status.OK)
final int nameColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_NAME); log.info("Got {}", result.toShortString());
final int latColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LAT); for (final Location l : result.getLocations())
final int lonColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LON); if (l.type == LocationType.STATION)
final int productsColumnIndex = cursor.getColumnIndex(NetworkContentProvider.KEY_PRODUCTS); stations.add(new Station(network, l));
final int linesColumnIndex = cursor.getColumnIndexOrThrow(NetworkContentProvider.KEY_LINES); } catch (final IOException x) {
x.printStackTrace();
while (cursor.moveToNext()) {
final String id = localIdColumnIndex != -1 ? cursor.getString(localIdColumnIndex)
: cursor.getString(nativeIdColumnIndex);
final String place = placeColumnIndex != -1 ? cursor.getString(placeColumnIndex) : null;
final String name = cursor.getString(nameColumnIndex);
if (id.equals(query) || (place != null && mQuery.reset(place).find())
|| mQuery.reset(name).find()) {
final int lat = cursor.getInt(latColumnIndex);
final int lon = cursor.getInt(lonColumnIndex);
final Point coord = Point.from1E6(lat, lon);
final Set<Product> products;
if (productsColumnIndex != -1 && !cursor.isNull(productsColumnIndex))
products = Product.fromCodes(cursor.getString(productsColumnIndex).toCharArray());
else
products = null;
final List<LineDestination> lineDestinations = new LinkedList<>();
for (final String lineStr : cursor.getString(linesColumnIndex).split(",")) {
if (!lineStr.isEmpty()) {
final Product product = Product.fromCode(lineStr.charAt(0));
final String label = Strings.emptyToNull(lineStr.substring(1));
final Style style = networkProvider.lineStyle(null, product, label);
lineDestinations
.add(new LineDestination(new Line(null, null, product, label, style), null));
}
}
final Location location = new Location(LocationType.STATION, id, coord, place, name, products);
stations.add(new Station(network, location, lineDestinations));
}
}
cursor.close();
} else {
try {
final SuggestLocationsResult result = networkProvider.suggestLocations(query,
EnumSet.of(LocationType.STATION), 0);
if (result.status == SuggestLocationsResult.Status.OK)
for (final Location l : result.getLocations())
if (l.type == LocationType.STATION)
stations.add(new Station(network, l));
} catch (final IOException x) {
x.printStackTrace();
}
} }
return stations; return stations;