From e1af67fd7862b5f0645ac79e55b47f5623ad98a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20FERREIRA=20DE=20SOUSA?= Date: Sun, 29 Nov 2020 18:08:56 +0100 Subject: [PATCH 1/3] Update osmdroid to v6.1.8 This commit aims to get Mapnik working for newer Android versions, which is done by upgrading osmdroid to latest. This update implies some porting work : - use Double instead of int argument when calling setMinZoomLevel. - enhance osmdroid configuration : call 'load' to ensure config (github.com/osmdroid/osmdroid/wiki/How-to-use-the-osmdroid-library) and customize user-agent to access tiles following recommendations (github.com/osmdroid/osmdroid/wiki/Important-notes-on-using-osmdroid- in-your-app#set-the-http-user-agent-variable). - replace the discarded OpenCycleMap tile source by HikeBikeMap. --- app/build.gradle | 2 +- .../openbikesharing/app/activities/MapActivity.java | 10 ++++++++-- .../app/activities/StationActivity.java | 9 ++++++++- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ffc81cf..4ef6f59 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ android { } dependencies { - compile 'org.osmdroid:osmdroid-android:5.6.4' + compile 'org.osmdroid:osmdroid-android:6.1.8' compile 'com.github.MKergall:osmbonuspack:6.3' compile 'com.android.support:support-v4:23.0.1' } diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java index 13a7fe7..da90d49 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java @@ -39,6 +39,7 @@ import org.osmdroid.api.IMapController; import org.osmdroid.bonuspack.clustering.GridMarkerClusterer; +import org.osmdroid.config.Configuration; import org.osmdroid.events.MapEventsReceiver; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.GeoPoint; @@ -53,6 +54,7 @@ import java.util.ArrayList; import be.brunoparmentier.openbikesharing.app.R; +import be.brunoparmentier.openbikesharing.app.BuildConfig; import be.brunoparmentier.openbikesharing.app.db.StationsDataSource; import be.brunoparmentier.openbikesharing.app.models.Station; import be.brunoparmentier.openbikesharing.app.models.StationStatus; @@ -96,6 +98,10 @@ protected void onCreate(Bundle savedInstanceState) { stationsDataSource = new StationsDataSource(this); ArrayList stations = stationsDataSource.getStations(); + final Context context = getApplicationContext(); + Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context)); + Configuration.getInstance().setUserAgentValue(BuildConfig.APPLICATION_ID); + map = (MapView) findViewById(R.id.mapView); stationMarkerInfoWindow = new StationMarkerInfoWindow(R.layout.bonuspack_bubble, map); @@ -118,7 +124,7 @@ protected void onCreate(Bundle savedInstanceState) { map.setMultiTouchControls(true); map.setBuiltInZoomControls(true); - map.setMinZoomLevel(3); + map.setMinZoomLevel(Double.valueOf(3)); /* map tile source */ String mapLayer = settings.getString(PREF_KEY_MAP_LAYER, ""); @@ -127,7 +133,7 @@ protected void onCreate(Bundle savedInstanceState) { map.setTileSource(TileSourceFactory.MAPNIK); break; case MAP_LAYER_CYCLEMAP: - map.setTileSource(TileSourceFactory.CYCLEMAP); + map.setTileSource(TileSourceFactory.HIKEBIKEMAP); break; case MAP_LAYER_OSMPUBLICTRANSPORT: map.setTileSource(TileSourceFactory.PUBLIC_TRANSPORT); diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java index 65c35a8..6225427 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java @@ -19,6 +19,7 @@ import android.app.Activity; import android.appwidget.AppWidgetManager; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -36,6 +37,7 @@ import android.widget.Toast; import org.osmdroid.api.IMapController; +import org.osmdroid.config.Configuration; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; @@ -48,6 +50,7 @@ import java.util.TimeZone; import be.brunoparmentier.openbikesharing.app.R; +import be.brunoparmentier.openbikesharing.app.BuildConfig; import be.brunoparmentier.openbikesharing.app.db.StationsDataSource; import be.brunoparmentier.openbikesharing.app.models.Station; import be.brunoparmentier.openbikesharing.app.models.StationStatus; @@ -79,6 +82,10 @@ protected void onCreate(Bundle savedInstanceState) { station = (Station) getIntent().getSerializableExtra(KEY_STATION); + final Context context = getApplicationContext(); + Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context)); + Configuration.getInstance().setUserAgentValue(BuildConfig.APPLICATION_ID); + map = (MapView) findViewById(R.id.mapView); final GeoPoint stationLocation = new GeoPoint((int) (station.getLatitude() * 1000000), (int) (station.getLongitude() * 1000000)); @@ -93,7 +100,7 @@ protected void onCreate(Bundle savedInstanceState) { map.setTileSource(TileSourceFactory.MAPNIK); break; case MAP_LAYER_CYCLEMAP: - map.setTileSource(TileSourceFactory.CYCLEMAP); + map.setTileSource(TileSourceFactory.HIKEBIKEMAP); break; case MAP_LAYER_OSMPUBLICTRANSPORT: map.setTileSource(TileSourceFactory.PUBLIC_TRANSPORT); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f86159..0785ad4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -121,7 +121,7 @@ Version Map layer Mapnik - OpenCycleMap + Hike & Bike Map OSMPublicTransport Strip station ID From 365a1757ab21a0795f3d558b7c9c4432abcab927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20FERREIRA=20DE=20SOUSA?= Date: Sun, 14 Jun 2020 17:57:38 +0200 Subject: [PATCH 2/3] Add support for electric bikes Currently on CityBikes API, electric bikes seem to be supported on 4 networks only, as evidenced in these files : - pybikes/bicing.py - pybikes/smovengo.py - pybikes/keolis.py - pybikes/smartbike.py It's not clear if it's standard but every one of them use the same architecture : the 'extra' JSON item of a station contains the two following sub-items : - 'has_ebikes' : boolean - 'ebikes' : integer Since on some networks the 'has_bikes' value is computed from the 'ebikes' value being 0 or not 0, only this very data is relevant. This commit add the 'ebikes' data as Station class's attributes as well as stations table's columns (upgrade database to version 2). It's copied from remote object if it exists, otherwise it is null. --- .../openbikesharing/app/db/DatabaseHelper.java | 9 +++++++-- .../openbikesharing/app/db/StationsDataSource.java | 13 +++++++++---- .../openbikesharing/app/models/Station.java | 10 ++++++++++ .../app/parsers/BikeNetworkParser.java | 5 +++++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/DatabaseHelper.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/DatabaseHelper.java index e97ee79..977a323 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/DatabaseHelper.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/DatabaseHelper.java @@ -25,7 +25,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { private static DatabaseHelper instance; private static final String DB_NAME = "openbikesharing.sqlite"; - private static final int DB_VERSION = 1; + private static final int DB_VERSION = 2; public static final String STATIONS_TABLE_NAME = "stations"; public static final String STATIONS_COLUMN_ID = "id"; @@ -39,6 +39,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { public static final String STATIONS_COLUMN_BANKING = "banking"; public static final String STATIONS_COLUMN_BONUS = "bonus"; public static final String STATIONS_COLUMN_STATUS = "status"; + public static final String STATIONS_COLUMN_EBIKES = "ebikes"; public static final String FAV_STATIONS_TABLE_NAME = "fav_stations"; public static final String FAV_STATIONS_COLUMN_ID = "id"; @@ -61,7 +62,8 @@ public void onCreate(SQLiteDatabase db) { + "(id TEXT PRIMARY KEY, name TEXT NOT NULL, last_update TEXT NOT NULL, " + "latitude NUMERIC NOT NULL, longitude NUMERIC NOT NULL, " + "free_bikes INTEGER NOT NULL, empty_slots INTEGER NOT NULL, " - + "address TEXT, banking INTEGER, bonus INTEGER, status TEXT)" + + "address TEXT, banking INTEGER, bonus INTEGER, status TEXT, " + + "ebikes INTEGER) " ); db.execSQL("CREATE TABLE " + FAV_STATIONS_TABLE_NAME + "(id TEXT PRIMARY KEY)" @@ -71,6 +73,9 @@ public void onCreate(SQLiteDatabase db) { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < 2) { + db.execSQL("ALTER TABLE " + STATIONS_TABLE_NAME + " ADD COLUMN ebikes INTEGER;"); + } } } diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/StationsDataSource.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/StationsDataSource.java index db1f84d..7a3dc85 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/StationsDataSource.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/db/StationsDataSource.java @@ -57,7 +57,9 @@ public void storeStations(ArrayList stations) { values.put(DatabaseHelper.STATIONS_COLUMN_BONUS, station.isBonus() ? 1 : 0); if (station.getStatus() != null) values.put(DatabaseHelper.STATIONS_COLUMN_STATUS, station.getStatus().name()); - + if (station.getEBikes() != null) { + values.put(DatabaseHelper.STATIONS_COLUMN_EBIKES, String.valueOf(station.getEBikes())); + } db.insert(DatabaseHelper.STATIONS_TABLE_NAME, null, values); } db.setTransactionSuccessful(); @@ -75,7 +77,7 @@ public ArrayList getStations() { SQLiteDatabase db = dbHelper.getReadableDatabase(); ArrayList stations = new ArrayList<>(); Cursor cursor = db.rawQuery("SELECT id as _id, name, last_update, latitude, longitude, " - + "free_bikes, empty_slots, address, banking, bonus, status " + + "free_bikes, empty_slots, address, banking, bonus, status, ebikes " + "FROM " + DatabaseHelper.STATIONS_TABLE_NAME, null); try { @@ -97,7 +99,7 @@ public Station getStation(String id) { SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("SELECT id as _id, name, last_update, latitude, longitude, " - + "free_bikes, empty_slots, address, banking, bonus, status " + + "free_bikes, empty_slots, address, banking, bonus, status, ebikes " + "FROM " + DatabaseHelper.STATIONS_TABLE_NAME + " " + "WHERE id = ?", new String[] { id }); try { @@ -127,7 +129,7 @@ public ArrayList getFavoriteStations() { SQLiteDatabase db = dbHelper.getReadableDatabase(); ArrayList favStations = new ArrayList<>(); Cursor cursor = db.rawQuery("SELECT sta.id as _id, name, last_update, latitude, longitude, " - + "free_bikes, empty_slots, address, banking, bonus, status " + + "free_bikes, empty_slots, address, banking, bonus, status, ebikes " + "FROM " + DatabaseHelper.FAV_STATIONS_TABLE_NAME + " sta " + "INNER JOIN " + DatabaseHelper.STATIONS_TABLE_NAME + " fav " + "ON sta.id = fav.id", null); @@ -181,6 +183,9 @@ private Station toStation(Cursor cursor) { if (!cursor.isNull(10)) { station.setStatus(StationStatus.valueOf(cursor.getString(10))); // status } + if (!cursor.isNull(11)) { + station.setEBikes(cursor.getInt(11)); // ebikes + } return station; } diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/models/Station.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/models/Station.java index 0fbf0cf..e64c638 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/models/Station.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/models/Station.java @@ -35,6 +35,7 @@ public class Station implements Serializable, Comparable { private Boolean banking; private Boolean bonus; private StationStatus status; + private Integer eBikes; public Station(String id, String name, String lastUpdate, double latitude, double longitude, int freeBikes, int emptySlots) { this.id = id; @@ -49,6 +50,7 @@ public Station(String id, String name, String lastUpdate, double latitude, doubl this.banking = null; this.bonus = null; this.status = null; + this.eBikes = null; } public String getId() { @@ -95,6 +97,10 @@ public Boolean isBonus() { return bonus; } + public Integer getEBikes() { + return eBikes; + } + public StationStatus getStatus() { return status; } @@ -111,6 +117,10 @@ public void setBonus(boolean bonus) { this.bonus = bonus; } + public void setEBikes(int eBikes) { + this.eBikes = eBikes; + } + @Override public int compareTo(Station another) { return name.compareToIgnoreCase(another.getName()) > 0 ? 1 : diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/parsers/BikeNetworkParser.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/parsers/BikeNetworkParser.java index f4d1bb3..3c2b89d 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/parsers/BikeNetworkParser.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/parsers/BikeNetworkParser.java @@ -145,6 +145,11 @@ public BikeNetworkParser(String toParse, boolean stripIdFromStationName) throws station.setStatus(StationStatus.OPEN); } } + + /* electric bikes */ + if (rawExtra.has("ebikes")) { + station.setEBikes(rawExtra.getInt("ebikes")); + } } stations.add(station); } From d88b618819ce783136e264b7d332e69c81cc3e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20FERREIRA=20DE=20SOUSA?= Date: Sun, 14 Jun 2020 17:59:22 +0200 Subject: [PATCH 3/3] Include electric bikes into GUI This commit handles display of electric bikes by adding an 'ebikes' area to the 'free bikes' and 'empty slots' legacy areas of the GUI. By default this additional area is non visible, and it shows up only if the bike system supports it. In this case, the 'free bikes' area is turned into a 'regular bikes' area : it displays the number of non-electric bikes only, and the icon is replaced by a green one. This update is done on 4 pieces of GUI's code : - StationActivity.java which inflates activity_station.xml - StationsListAdapter.java which inflates station_list_item.xml - MapActivity.java which inflates bonuspack_bubble.xml - StationsListAppWidgetService.java which inflates app_widget_item.xml --- .../app/activities/MapActivity.java | 18 ++++++++++++ .../app/activities/StationActivity.java | 14 ++++++++++ .../app/adapters/StationsListAdapter.java | 17 ++++++++++-- .../widgets/StationsListAppWidgetService.java | 17 ++++++++++-- .../res/drawable-hdpi/ic_electric_bike.png | Bin 0 -> 3434 bytes .../res/drawable-hdpi/ic_regular_bike.png | Bin 0 -> 3185 bytes .../res/drawable-mdpi/ic_electric_bike.png | Bin 0 -> 1953 bytes .../res/drawable-mdpi/ic_regular_bike.png | Bin 0 -> 1856 bytes .../res/drawable-xhdpi/ic_electric_bike.png | Bin 0 -> 5092 bytes .../res/drawable-xhdpi/ic_regular_bike.png | Bin 0 -> 4830 bytes .../res/drawable-xxhdpi/ic_electric_bike.png | Bin 0 -> 9353 bytes .../res/drawable-xxhdpi/ic_regular_bike.png | Bin 0 -> 8992 bytes app/src/main/res/layout/activity_station.xml | 26 ++++++++++++++++-- app/src/main/res/layout/app_widget_item.xml | 19 +++++++++++++ app/src/main/res/layout/bonuspack_bubble.xml | 20 +++++++++++++- app/src/main/res/layout/station_list_item.xml | 18 ++++++++++++ app/src/main/res/values/strings.xml | 1 + 17 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_electric_bike.png create mode 100644 app/src/main/res/drawable-hdpi/ic_regular_bike.png create mode 100644 app/src/main/res/drawable-mdpi/ic_electric_bike.png create mode 100644 app/src/main/res/drawable-mdpi/ic_regular_bike.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_electric_bike.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_regular_bike.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_electric_bike.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_regular_bike.png diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java index da90d49..c3538bb 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/MapActivity.java @@ -35,6 +35,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; import org.osmdroid.api.IMapController; @@ -289,6 +290,23 @@ public void onOpen(Object item) { ImageView emptySlotsLogo = (ImageView) getView().findViewById(R.id.bubble_emptyslots_logo); emptySlotsLogo.setVisibility(View.GONE); } + + ImageView regularBikesLogo = (ImageView) getView().findViewById(R.id.bubble_freebikes_logo); + TextView regularBikesValue = (TextView) getView().findViewById(R.id.bubble_description); + ImageView eBikesLogo = (ImageView) getView().findViewById(R.id.bubble_ebikes_logo); + TextView eBikesValue = (TextView) getView().findViewById(R.id.bubble_ebikes_value); + + int bikes = markerStation.getFreeBikes(); + if(markerStation.getEBikes() != null && regularBikesLogo != null + && eBikesLogo != null && regularBikesValue!= null && eBikesValue != null ) { + int ebikes = markerStation.getEBikes(); + regularBikesValue.setText(String.valueOf(bikes-ebikes)); + regularBikesLogo.setImageResource(R.drawable.ic_regular_bike); + eBikesLogo.setVisibility(View.VISIBLE); + eBikesValue.setVisibility(View.VISIBLE); + eBikesValue.setText(String.valueOf(ebikes)); + } + layout.setClickable(true); layout.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java index 6225427..0f4b028 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/activities/StationActivity.java @@ -171,6 +171,7 @@ public boolean onMarkerClick(Marker marker, MapView mapView) { Boolean isBankingStation = station.isBanking(); Boolean isBonusStation = station.isBonus(); StationStatus stationStatus = station.getStatus(); + Integer stationEBikes = station.getEBikes(); if (isBankingStation != null) { ImageView stationBanking = (ImageView) findViewById(R.id.stationBanking); @@ -208,6 +209,19 @@ public void onClick(View view) { stationName.setPaintFlags(stationName.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } + if (stationEBikes != null) { + ImageView eBikesLogo = (ImageView) findViewById(R.id.stationEBikesLogo); + ImageView regularBikesLogo = (ImageView) findViewById(R.id.stationFreeBikesLogo); + TextView stationEBikesValue = (TextView) findViewById(R.id.stationEBikesValue); + int ebikes = station.getEBikes(); + + eBikesLogo.setVisibility(View.VISIBLE); + stationEBikesValue.setVisibility(View.VISIBLE); + stationEBikesValue.setText(String.valueOf(ebikes)); + regularBikesLogo.setImageResource(R.drawable.ic_regular_bike); + stationFreeBikes.setText(String.valueOf(freeBikes - ebikes)); //display regular bikes only + } + mapController.setCenter(stationLocation); } diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/adapters/StationsListAdapter.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/adapters/StationsListAdapter.java index e5315a8..890517d 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/adapters/StationsListAdapter.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/adapters/StationsListAdapter.java @@ -57,6 +57,9 @@ public View getView(int position, View convertView, ViewGroup parent) { if (station != null) { TextView stationNameTitle = (TextView) v.findViewById(R.id.stationNameTitle); TextView freeBikesValue = (TextView) v.findViewById(R.id.freeBikesValue); + ImageView regularBikesLogo = (ImageView) v.findViewById(R.id.freeBikesLogo); + TextView freeEBikesValue = (TextView) v.findViewById(R.id.freeEBikesValue); + ImageView eBikesLogo = (ImageView) v.findViewById(R.id.freeEBikesLogo); TextView emptySlotsValue = (TextView) v.findViewById(R.id.emptySlotsValue); StationStatus stationStatus = station.getStatus(); @@ -72,7 +75,17 @@ public View getView(int position, View convertView, ViewGroup parent) { if (freeBikesValue != null) { int bikes = station.getFreeBikes(); - freeBikesValue.setText(String.valueOf(bikes)); + if(station.getEBikes() != null && regularBikesLogo != null + && eBikesLogo != null && freeEBikesValue != null ) { + int ebikes = station.getEBikes(); + freeBikesValue.setText(String.valueOf(bikes-ebikes)); + regularBikesLogo.setImageResource(R.drawable.ic_regular_bike); + eBikesLogo.setVisibility(View.VISIBLE); + freeEBikesValue.setVisibility(View.VISIBLE); + freeEBikesValue.setText(String.valueOf(ebikes)); + } else { + freeBikesValue.setText(String.valueOf(bikes)); + } } if (emptySlotsValue != null) { @@ -91,4 +104,4 @@ public View getView(int position, View convertView, ViewGroup parent) { return v; } -} \ No newline at end of file +} diff --git a/app/src/main/java/be/brunoparmentier/openbikesharing/app/widgets/StationsListAppWidgetService.java b/app/src/main/java/be/brunoparmentier/openbikesharing/app/widgets/StationsListAppWidgetService.java index 82c329d..59f4f3c 100644 --- a/app/src/main/java/be/brunoparmentier/openbikesharing/app/widgets/StationsListAppWidgetService.java +++ b/app/src/main/java/be/brunoparmentier/openbikesharing/app/widgets/StationsListAppWidgetService.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.view.View; import android.widget.RemoteViews; import android.widget.RemoteViewsService; @@ -88,9 +89,19 @@ public RemoteViews getViewAt(int position) { // We construct a remote views item based on our widget item xml file, and set the // text based on the position. RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.app_widget_item); - rv.setTextViewText(R.id.widgetStationNameTitle, stations.get(position).getName()); - rv.setTextViewText(R.id.widgetFreeBikesValue, String.valueOf(stations.get(position).getFreeBikes())); - rv.setTextViewText(R.id.widgetEmptySlotsValue, String.valueOf(stations.get(position).getEmptySlots())); + Station mStation = stations.get(position); + rv.setTextViewText(R.id.widgetStationNameTitle, mStation.getName()); + int bikes = mStation.getFreeBikes(); + if(mStation.getEBikes() != null) { + int ebikes = mStation.getEBikes(); + rv.setImageViewResource(R.id.widgetFreeBikesLogo, R.drawable.ic_regular_bike); + rv.setViewVisibility(R.id.widgetEBikesLogo, View.VISIBLE); + rv.setViewVisibility(R.id.widgetEBikesValue, View.VISIBLE); + rv.setTextViewText(R.id.widgetEBikesValue, String.valueOf(ebikes)); + bikes = bikes - ebikes; + } + rv.setTextViewText(R.id.widgetFreeBikesValue, String.valueOf(bikes)); + rv.setTextViewText(R.id.widgetEmptySlotsValue, String.valueOf(mStation.getEmptySlots())); // Next, we set a fill-intent which will be used to fill-in the pending intent template // which is set on the collection view in StationsListAppWidgetProvider. diff --git a/app/src/main/res/drawable-hdpi/ic_electric_bike.png b/app/src/main/res/drawable-hdpi/ic_electric_bike.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf9cfc155a182d5665c014daaa8871ff9470e87 GIT binary patch literal 3434 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 M07*qoM6N<$f_%q!2><{9 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_regular_bike.png b/app/src/main/res/drawable-hdpi/ic_regular_bike.png new file mode 100644 index 0000000000000000000000000000000000000000..b1bf40ba40b030e9ca09981b94a28097598259a4 GIT binary patch literal 3185 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 X<(*fdcv;f?00000NkvXXu0mjfTx=#I literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_electric_bike.png b/app/src/main/res/drawable-mdpi/ic_electric_bike.png new file mode 100644 index 0000000000000000000000000000000000000000..b36a30f1a6db9918f8747e56282aa1294e1d8ff8 GIT binary patch literal 1953 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 + + + + @@ -116,7 +138,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_column="1" - android:layout_row="2" + android:layout_row="3" android:text="5" android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/app/src/main/res/layout/app_widget_item.xml b/app/src/main/res/layout/app_widget_item.xml index e3f275c..5e19ef4 100644 --- a/app/src/main/res/layout/app_widget_item.xml +++ b/app/src/main/res/layout/app_widget_item.xml @@ -61,6 +61,25 @@ android:textColor="@android:color/primary_text_light" android:textAppearance="?android:attr/textAppearanceMedium" /> + + + + + android:rowCount="3"> + + + + + + + + Free bikes:\u00A0 + Electric bikes:\u00A0 Empty slots:\u00A0 All stations Favorites