From c7c3a4b18a909afb92a48069391884d875e3c0a6 Mon Sep 17 00:00:00 2001 From: doperez Date: Sat, 17 Nov 2018 08:07:34 -0600 Subject: [PATCH 1/6] Refactored code to target Android API 28, and refactored all findViewById() calls to use @BindView and ButterKnife.bind(). --- .idea/assetWizardSettings.xml | 52 ------------------ .idea/caches/build_file_checksums.ser | Bin 536 -> 535 bytes .idea/misc.xml | 8 ++- app/build.gradle | 23 +++++--- app/src/debug/res/values/google_maps_api.xml | 2 +- .../arcu/razorbacktransit/MainActivity.java | 19 ++++--- .../razorbacktransit/ParkingMapFragment.java | 16 ++++-- .../razorbacktransit/ViewRouteFragment.java | 7 ++- .../ViewScheduleFragment.java | 9 +-- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 11 files changed, 56 insertions(+), 86 deletions(-) delete mode 100644 .idea/assetWizardSettings.xml diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml deleted file mode 100644 index 8b8b96b..0000000 --- a/.idea/assetWizardSettings.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 9601e05ba721b647e43db5d8028c5440c7fdb99f..641afa3eaa654d41991a7141bb00552a9e96db41 100644 GIT binary patch delta 188 zcmbQiGM#0@bk=mmYduO6=lG|o>1X8Urs}68=4Gbl=O&~Kf};F_)S{Bi z)Z)@22Bv7IHJ`!lYZrsSlS7BMhIJFWRF{)3GpOk7^Q`;$oAaqXV}E@vVn8Pn;%YfB@8^1 zKQM|3a5QZHoW1AU^YwLaLtB?*7EJDAlod!a`I7Nb_KIv&4ojDy(%H$AcQbkmp08NC Tt$bhNiMv~8&Jtlye^>zkGfzb* diff --git a/.idea/misc.xml b/.idea/misc.xml index 99202cc..e0d5b93 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,22 +5,26 @@ diff --git a/app/build.gradle b/app/build.gradle index 7fbdb17..45fcbcc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 27 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { applicationId "razorbacktransit.arcu.razorbacktransit" minSdkVersion 19 - targetSdkVersion 27 + targetSdkVersion 28 versionCode 17 versionName "4.2.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -27,19 +27,24 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.google.firebase:firebase-messaging:17.3.0' + implementation 'com.google.firebase:firebase-core:16.0.5' + implementation 'com.google.firebase:firebase-messaging:17.3.4' + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - implementation 'com.android.support:appcompat-v7:27.1.1' + + implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.github.barteksc:android-pdf-viewer:2.8.1' - implementation 'com.android.support:design:27.1.1' + implementation 'com.android.support:design:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.2' - implementation 'com.android.support:support-v4:27.1.1' - implementation 'com.google.firebase:firebase-core:15.0.0' - implementation 'com.google.android.gms:play-services-maps:15.0.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.google.android.gms:play-services-maps:16.0.0' implementation 'com.squareup.okhttp3:okhttp:3.10.0' testImplementation 'junit:junit:4.12' + + implementation 'com.jakewharton:butterknife:9.0.0-rc1' + annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1' } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/debug/res/values/google_maps_api.xml b/app/src/debug/res/values/google_maps_api.xml index b669533..7072663 100644 --- a/app/src/debug/res/values/google_maps_api.xml +++ b/app/src/debug/res/values/google_maps_api.xml @@ -21,6 +21,6 @@ string in this file. --> - AIzaSyCw6Ph5uz-2fnVzVMFDIZs0B8aBXONywOk + AIzaSyCJ_t16aFkINz-nsI1T2BMoP6EPrz1iM80 diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java index d30914d..f1da0cc 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java @@ -14,6 +14,9 @@ import com.google.firebase.analytics.FirebaseAnalytics; +import butterknife.BindView; +import butterknife.ButterKnife; + public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, LiveMapFragment.OnFragmentInteractionListener, @@ -21,10 +24,12 @@ public class MainActivity extends AppCompatActivity SchedulesFragment.OnFragmentInteractionListener, RoutesFragment.OnFragmentInteractionListener, ViewScheduleFragment.OnFragmentInteractionListener, - ViewRouteFragment.OnFragmentInteractionListener { + ViewRouteFragment.OnFragmentInteractionListener +{ - int lastMenuItemId = 10; - private NavigationView navigationView; + @BindView(R.id.nav_view) NavigationView navigationView; + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.drawer_layout) DrawerLayout drawer; private LiveMapFragment liveMapFragment; private SchedulesFragment schedulesFragment; private RoutesFragment routesFragment; @@ -33,6 +38,7 @@ public class MainActivity extends AppCompatActivity private ViewRouteFragment viewRouteFragment; private FirebaseAnalytics mFirebaseAnalytics; private final FragmentManager fragmentManager = getSupportFragmentManager(); + int lastMenuItemId = 10; @Override protected void onCreate(Bundle savedInstanceState) { @@ -41,16 +47,14 @@ protected void onCreate(Bundle savedInstanceState) { mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); setTheme(R.style.AppTheme_NoActionBar); setContentView(R.layout.activity_main); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + ButterKnife.bind(this); setSupportActionBar(toolbar); - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.setDrawerListener(toggle); toggle.syncState(); - navigationView = (NavigationView) findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); liveMapFragment = new LiveMapFragment(); @@ -70,7 +74,6 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onBackPressed() { - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { @@ -88,7 +91,6 @@ public boolean onNavigationItemSelected(MenuItem item) { if (lastMenuItemId == navigationView.getMenu().getItem(1).getItemId() || lastMenuItemId == navigationView.getMenu().getItem(2).getItemId()) { lastMenuItemId = id; } else { - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; } @@ -213,7 +215,6 @@ public boolean onNavigationItemSelected(MenuItem item) { } } - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; } diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java index ab10756..985fb0a 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java @@ -12,6 +12,9 @@ import java.io.InputStream; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; /** @@ -24,9 +27,9 @@ */ public class ParkingMapFragment extends Fragment { - private PDFView pdfView; + @BindView(R.id.pdfView) PDFView pdfView; + private Unbinder unbinder; private Boolean loaded; - private OnFragmentInteractionListener mListener; public ParkingMapFragment() { @@ -53,8 +56,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_parking_map, container, false); - - pdfView = (PDFView)view.findViewById(R.id.pdfView); + unbinder = ButterKnife.bind(this, view); InputStream inputStream = getResources().openRawResource( getResources().getIdentifier("parkmap", @@ -97,6 +99,12 @@ public void onDetach() { mListener = null; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + /** * This interface must be implemented by activities that contain this * fragment to allow an interaction in this fragment to be communicated diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java index d924195..2affcf0 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java @@ -12,6 +12,9 @@ import java.io.InputStream; +import butterknife.BindView; +import butterknife.ButterKnife; + /** * A simple {@link Fragment} subclass. @@ -27,7 +30,7 @@ public class ViewRouteFragment extends Fragment { private static final String ARG_PARAM1 = "filepath"; private String mParam1; - private PDFView pdfView; + @BindView(R.id.pdfView) PDFView pdfView; private OnFragmentInteractionListener mListener; @@ -56,7 +59,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_view_route, container, false); - pdfView = (PDFView)view.findViewById(R.id.pdfView); + ButterKnife.bind(this, view); InputStream inputStream = getResources().openRawResource( getResources().getIdentifier(mParam1, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java index f82999d..6b88edc 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java @@ -13,6 +13,9 @@ import java.io.InputStream; +import butterknife.BindView; +import butterknife.ButterKnife; + /** * A simple {@link Fragment} subclass. @@ -24,11 +27,9 @@ */ public class ViewScheduleFragment extends Fragment { + @BindView(R.id.pdfView) PDFView pdfView; private static final String ARG_PARAM1 = "filepath"; - private String mParam1; - private PDFView pdfView; - private OnFragmentInteractionListener mListener; public ViewScheduleFragment() { @@ -57,7 +58,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_view_schedule, container, false); - pdfView = (PDFView)view.findViewById(R.id.pdfView); + ButterKnife.bind(this, view); InputStream inputStream = getResources().openRawResource( getResources().getIdentifier(mParam1, diff --git a/build.gradle b/build.gradle index 558fdf8..c6ab89b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' + classpath 'com.android.tools.build:gradle:3.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8f43cb3..624b7bf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Jun 19 16:57:27 CDT 2018 +#Sat Nov 17 07:14:38 CST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip From 9bf3f7be4a93bd6034274ad352e876c8a3ee58e8 Mon Sep 17 00:00:00 2001 From: doperez Date: Sun, 18 Nov 2018 02:53:10 -0600 Subject: [PATCH 2/6] Set up refreshing bus routes to use RxJava, Retrofit, and refactored LiveMapFragment to be in Kotlin to use lambdas not available in Java 7. --- .idea/caches/build_file_checksums.ser | Bin 535 -> 535 bytes app/build.gradle | 28 +- .../ExampleInstrumentedTest.java | 4 +- .../razorbacktransit/LiveMapFragment.java | 643 ------------------ .../arcu/razorbacktransit/LiveMapFragment.kt | 229 +++++++ .../arcu/razorbacktransit/MainActivity.java | 18 +- .../MyFirebaseMessagingService.java | 7 +- .../arcu/razorbacktransit/PDFFactory.java | 4 +- .../razorbacktransit/ParkingMapFragment.java | 2 +- .../arcu/razorbacktransit/RoutesFragment.java | 4 +- .../razorbacktransit/SchedulesFragment.java | 4 +- .../razorbacktransit/ViewRouteFragment.java | 2 +- .../ViewScheduleFragment.java | 2 +- .../arcu/razorbacktransit/model/bus/Bus.kt | 21 + .../razorbacktransit/model/bus/BusJson.kt | 44 ++ .../model/bus/BusJsonAdapter.kt | 8 + .../model/busimage/BusImage.kt | 5 + .../razorbacktransit/model/route/Route.kt | 17 + .../razorbacktransit/model/route/RouteJson.kt | 52 ++ .../model/route/RouteJsonAdapter.kt | 8 + .../model/stopimage/StopImage.kt | 5 + .../razorbacktransit/network/CampusService.kt | 36 + .../razorbacktransit/network/NetworkState.kt | 19 + .../razorbacktransit/network/StartEvent.kt | 3 + .../razorbacktransit/network/TransitStream.kt | 77 +++ .../arcu/razorbacktransit/utils/Utils.kt | 32 + app/src/main/res/layout/activity_main.xml | 6 +- app/src/main/res/layout/app_bar_main.xml | 10 +- app/src/main/res/layout/content_main.xml | 4 +- build.gradle | 2 + gradle.properties | 2 + 31 files changed, 613 insertions(+), 685 deletions(-) delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.java create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJsonAdapter.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJsonAdapter.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 641afa3eaa654d41991a7141bb00552a9e96db41..82c9e454f79c273c3f1d82bbc17cab614291bc06 100644 GIT binary patch delta 95 zcmV-l0HFVu1eXMmm;@zaB>9n?;t=AQJwDty6g*9UPO0%>({_`N0UZ#A^tAc_?bXp2 zo(@50`3PK-y8#{$ZR|IcibAsrAtSJ`NuATNlj#8+5mbwa5i<+W>@b<|Hg)c0g?Q)% BD!Tvx delta 95 zcmV-l0HFVu1eXMmm;@o#j3SYo;t;FS3hKL4vxR^;3Epxq0XCD40UZ#GNVVgufx1pp zA?E_ju)FA!y8#{$w2fX9*7b busMarkers = new ArrayList<>(); - private final HashMap stopMarkerHashMap = new HashMap<>(); - private List routeIDsForBusses = new ArrayList<>(); - private Timer busTimer; - - private int stopImageWidth; - private int stopImageHeight; - private int busImageWidth; - private int busImageHeight; - - public LiveMapFragment() { - // Required empty public constructor - } - - public static LiveMapFragment newInstance(String param1, String param2) { - return new LiveMapFragment(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - try { - sharedPreferences = getActivity().getPreferences(Context.MODE_PRIVATE); - } catch (NullPointerException e) { - e.printStackTrace(); - } - - editor = sharedPreferences.edit(); - - final int widthPixels = getActivity().getResources().getDisplayMetrics().widthPixels; - - stopImageWidth = (int) (widthPixels * 0.02638888889); - stopImageHeight = (int) (widthPixels * 0.02638888889); - - busImageWidth = (int) (widthPixels * 0.05833333333); - busImageHeight = (int) (widthPixels * 0.05833333333 * 1.6153846154); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View view = inflater.inflate(R.layout.fragment_maps, container, false); - SupportMapFragment mapFragment = (SupportMapFragment) this.getChildFragmentManager() - .findFragmentById(R.id.map); - mapFragment.getMapAsync(this); - - return view; - } - - @Override - public void onResume() { - super.onResume(); - - if (this.googleMap != null) { - this.googleMap.clear(); - } - - try { - loadRoutes(); - loadBusses(); - } catch (Exception e) { - e.printStackTrace(); - } - - busTimer = new Timer(); - busTimer.schedule(new TimerTask() { - @Override - public void run() { - try { - loadBusses(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 5000, 5000); - } - - @Override - public void onPause() { - super.onPause(); - - editor.apply(); - busTimer.cancel(); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof OnFragmentInteractionListener) { - mListener = (OnFragmentInteractionListener) context; - } else { - throw new RuntimeException(context.toString() - + " must implement OnFragmentInteractionListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - - @Override - public void onMapReady(GoogleMap googleMap) { - this.googleMap = googleMap; - - this.googleMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(36.09, -94.1785))); - this.googleMap.moveCamera(CameraUpdateFactory.zoomTo(12.0f)); - this.googleMap.setMinZoomPreference(10); - this.googleMap.setOnMarkerClickListener(this); - UiSettings uiSettings = this.googleMap.getUiSettings(); - uiSettings.setRotateGesturesEnabled(true); - uiSettings.setTiltGesturesEnabled(false); - - } - - public void loadRoutes() throws Exception { - - Request request = new Request.Builder() - .url(getString(R.string.route_url)) - .build(); - - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - String responseText = responseBody.string(); - responseText = responseText.substring(7, responseText.length() - 2); - - try { - final JSONArray jsonArray = new JSONArray(responseText); - - Runnable myRunnable = new Runnable() { - @Override - public void run() { - - List routeIDs = new ArrayList<>(); - for (int i = 0; i < jsonArray.length(); i++) { - - try { - final JSONObject jsonObject = jsonArray.getJSONObject(i); - if (jsonObject.getString("inService").equals("1")) { - - List points = new ArrayList<>(); - String[] coordinates = jsonObject.getString("shape").split(","); - - if (coordinates.length > 1) { - for (String coordinate : coordinates) { - String[] latlong = coordinate.split(" "); - points.add(new LatLng( - Double.parseDouble(latlong[0]), - Double.parseDouble(latlong[1]))); - } - final Polyline polyline = googleMap.addPolyline(new PolylineOptions() - .color((int) Long.parseLong(("99" + jsonObject.getString("color") - .substring(1)), 16))); - polyline.setPoints(points); - } - if (!routeIDs.contains(jsonObject.getString("id"))) { - routeIDs.add(jsonObject.getString("id")); - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - try { - routeIDsForBusses = new ArrayList<>(routeIDs); - loadBusses(); - loadStops(routeIDs); - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - new Handler(Looper.getMainLooper()).post(myRunnable); - - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - }); - } - - private String buildStopURL(List routeIDs) { - String stopString = getString(R.string.stop_url); - for (String id : routeIDs) { - stopString = stopString.concat("-" + id); - } - return stopString; - } - - private String buildStopImageULR(String id, List routeIDs) { - String urlString = "https://campusdata.uark.edu/api/stopimages?stopId=" + id + "&routeIds=undefined"; - for (String routeID : routeIDs) { - urlString = urlString.concat("-" + routeID); - } - return urlString; - } - - public void loadStops(final List routeIDs) throws Exception { - - Request request = new Request.Builder() - .url(buildStopURL(routeIDs)) - .build(); - - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - String responseText = responseBody.string(); - responseText = responseText.substring(10, responseText.length() - 2); - - try { - final JSONArray jsonArray = new JSONArray(responseText); - stopMarkerHashMap.clear(); - - if (getActivity() == null) { - return; - } - - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - - for (int i = 0; i < jsonArray.length(); i++) { - - try { - final JSONObject jsonObject = jsonArray.getJSONObject(i); - - final Marker marker = googleMap.addMarker(new MarkerOptions() - .flat(true) - .alpha(0) - .snippet(jsonObject.getString("name")) - .title("Next Arrival: ...") - .position(new LatLng( - Double.parseDouble(jsonObject.getString("latitude")), - Double.parseDouble(jsonObject.getString("longitude"))))); - - stopMarkerHashMap.put(marker, jsonObject.getString("id")); - String encodedImage = sharedPreferences.getString(jsonObject.getString("id") + "1", ""); - - if (!encodedImage.equals("")) { - byte[] bytes = Base64.decode(encodedImage, Base64.DEFAULT); - final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - final BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. - fromBitmap(bitmap); - marker.setIcon(bitmapDescriptor); - marker.setAlpha(1); - } - - Request iconRequest = new Request - .Builder() - .url(buildStopImageULR(jsonObject.getString("id"), routeIDs)) - .build(); - - client.newCall(iconRequest).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - InputStream inputStream = response.body().byteStream(); - final Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(inputStream), stopImageWidth, stopImageHeight, true); - final BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. - fromBitmap(bitmap); - - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); - byte[] bytes = byteArrayOutputStream.toByteArray(); - - editor.putString(jsonObject.getString("id") + "1", Base64.encodeToString(bytes, Base64.DEFAULT)); - - if (getActivity() == null) { - return; - } - - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - try { - marker.setIcon(bitmapDescriptor); - marker.setAlpha(1); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - }); - } catch (JSONException e) { - e.printStackTrace(); - } - } - }); - - } catch (JSONException e) { - e.printStackTrace(); - } - - LatLngBounds.Builder builder = new LatLngBounds.Builder(); - - for (Marker marker : stopMarkerHashMap.keySet().toArray(new Marker[]{})) { - builder.include(marker.getPosition()); - } - LatLngBounds bounds = builder.build(); - int padding = busImageHeight; // offset from edges of the map in pixels - CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding); - googleMap.animateCamera(cu); - } - } - - }); - } catch (JSONException e) { - e.printStackTrace(); - } - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - }); - } - - public String getBusImageKey(String heading, String color) { - Double x = Double.parseDouble(heading); - return color + Integer.toString(((x.intValue() + 29) / 30) * 30); - } - - private String buildBusURL() { - String part1 = "https://campusdata.uark.edu/api/buses?callback=jQuery18002674589609856972_1510069338014&routeIds=undefined"; - String part2 = "&_=1510069339459"; - for (String id : routeIDsForBusses) { - part1 = part1.concat("-" + id); - } - part1 = part1.concat(part2); - return part1; - } - - public void loadBusses() throws Exception { - - Request request = new Request.Builder() - .url(buildBusURL()) - .build(); - - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - String responseText = responseBody.string(); - responseText = responseText.substring(41, responseText.length() - 2); - - try { - final JSONArray jsonArray = new JSONArray(responseText); - - if (getActivity() == null) { - return; - } - - getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - - List oldMarkers = new ArrayList<>(busMarkers); - busMarkers.clear(); - - for (int i = 0; i < jsonArray.length(); i++) { - - try { - final JSONObject jsonObject = jsonArray.getJSONObject(i); - - - - final Marker marker = googleMap.addMarker(new MarkerOptions() - .position(new LatLng( - Double.parseDouble(jsonObject.getString("latitude")), - Double.parseDouble(jsonObject.getString("longitude")))) - .flat(true) - .alpha(0) - .title(jsonObject.getString("routeName"))); - - busMarkers.add(marker); - final String key = getBusImageKey(jsonObject.getString("heading"), jsonObject.getString("color")); - final String encodedImage = sharedPreferences.getString(key, ""); - - if (!encodedImage.equals("")) { - - byte[] bytes = Base64.decode(encodedImage, Base64.DEFAULT); - final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); - final BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. - fromBitmap(bitmap); - marker.setIcon(bitmapDescriptor); - marker.setAlpha(1); - - } else { - - Request iconRequest = new Request - .Builder() - .url("https://campusdata.uark.edu/api/busimages?color=" + jsonObject.getString("color").substring(1) + "&heading=" + jsonObject.getString("heading")) - .build(); - - client.newCall(iconRequest).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - InputStream inputStream = response.body().byteStream(); - final Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(inputStream), busImageWidth, busImageHeight, true); - final BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. - fromBitmap(bitmap); - - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); - byte[] bytes = byteArrayOutputStream.toByteArray(); - - editor.putString(key, Base64.encodeToString(bytes, Base64.DEFAULT)); - editor.apply(); - - if (getActivity() == null) { - return; - } - - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - if (marker.isVisible()) { - try { - marker.setIcon(bitmapDescriptor); - marker.setAlpha(1); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - } - }); - } - } - }); - - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - for (Marker temp : oldMarkers) { - try { - temp.remove(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - } - }); - } catch (JSONException e) { - e.printStackTrace(); - } - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - }); - } - - @Override - public boolean onMarkerClick(final Marker marker) { - - if (!stopMarkerHashMap.containsKey(marker)) { - return false; - } - - marker.setTitle("Next Arrival: Loading..."); - marker.showInfoWindow(); - - final String url = "https://campusdata.uark.edu/api/routes?callback=jQuery18004251280482585251_1507605405541&stopId=" + stopMarkerHashMap.get(marker) + "&_=1507605550296"; - - final Request request = new Request.Builder() - .url(url) - .build(); - - client.newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); - - String responseText = responseBody.string(); - responseText = responseText.substring(41, responseText.length() - 2); - try { - - final JSONArray jsonArray = new JSONArray(responseText); - - if (getActivity() == null) { - return; - } - - getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - - for (int i = 0; i < jsonArray.length(); i++) { - - try { - JSONObject jsonObject = jsonArray.getJSONObject(i); - - final String nextArrival = jsonObject.getString("nextArrival"); - - if (!nextArrival.equals("...") && !nextArrival.equals("null")) { - - marker.setTitle("Next Arival: " + nextArrival); - marker.showInfoWindow(); - - return; - } - - } catch (JSONException e) { - e.printStackTrace(); - } - } - marker.setTitle("Next Arrival: None"); - marker.showInfoWindow(); - } - }); - } catch (JSONException e) { - e.printStackTrace(); - } - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - }); - return false; - } - - public interface OnFragmentInteractionListener { - // TODO: Update argument type and name - void onFragmentInteraction(Uri uri); - } -} diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt new file mode 100644 index 0000000..42e6db5 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt @@ -0,0 +1,229 @@ +package razorbacktransit.arcu.razorbacktransit + +import android.content.Context +import android.content.SharedPreferences +import android.graphics.BitmapFactory +import android.graphics.Color +import android.net.Uri +import android.os.Bundle +import android.util.Base64 +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import com.google.android.gms.maps.CameraUpdateFactory +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.SupportMapFragment +import com.google.android.gms.maps.model.* +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.network.TransitStream +import razorbacktransit.arcu.razorbacktransit.utils.clearMarkers +import java.util.* + +class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener +{ + private var mListener: OnFragmentInteractionListener? = null + private lateinit var googleMap: GoogleMap + private var sharedPreferences: SharedPreferences? = null + private var editor: SharedPreferences.Editor? = null + + private val stopMarkerHashMap = HashMap() + private val markers = arrayListOf() + private val routeIds = arrayListOf() + + private var stopImageWidth: Int = 0 + private var stopImageHeight: Int = 0 + private var busImageWidth: Int = 0 + private var busImageHeight: Int = 0 + private val disposables = CompositeDisposable() + + + override fun onCreate(savedInstanceState: Bundle?) + { + super.onCreate(savedInstanceState) + + sharedPreferences = activity!!.getPreferences(Context.MODE_PRIVATE) + editor = sharedPreferences!!.edit() + + val widthPixels = activity!!.resources.displayMetrics.widthPixels + + stopImageWidth = (widthPixels * 0.02638888889).toInt() + stopImageHeight = (widthPixels * 0.02638888889).toInt() + + busImageWidth = (widthPixels * 0.05833333333).toInt() + busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() + + // Update the UI for the routes + disposables += TransitStream.routes + .map { it.filter { it.name == "green" } } + .observeOn( AndroidSchedulers.mainThread() ) + .subscribe( this::updateRoutes ) + + // Update the UI for the buses + disposables += TransitStream.buses + .map { buses -> buses.filter { bus -> routeIds.contains( bus.id ) } } + .observeOn( AndroidSchedulers.mainThread() ) + .subscribe( this::updateBusses ) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? + { + + val view = inflater.inflate(R.layout.fragment_maps, container, false) + val mapFragment = this.childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment? + mapFragment!!.getMapAsync(this) + + return view + } + + override fun onResume() + { + super.onResume() + + if( this::googleMap.isInitialized ) + { + googleMap.clear() + } + } + + override fun onPause() + { + super.onPause() + editor!!.apply() + } + + override fun onAttach( context: Context? ) + { + super.onAttach(context) + if (context is OnFragmentInteractionListener) + { + mListener = context + } + else + { + throw RuntimeException(context!!.toString() + " must implement OnFragmentInteractionListener") + } + } + + override fun onDetach() + { + super.onDetach() + mListener = null + disposables.dispose() + } + + override fun onMapReady( map: GoogleMap ) + { + googleMap = map + googleMap.apply { + moveCamera( CameraUpdateFactory.newLatLng( LatLng(36.09, -94.1785) ) ) + moveCamera(CameraUpdateFactory.zoomTo(12.0f)) + setMinZoomPreference(10f) + setOnMarkerClickListener(this@LiveMapFragment) + uiSettings.isRotateGesturesEnabled = true + uiSettings.isTiltGesturesEnabled = false + } + } + + @MainThread + private fun updateRoutes( routes: List ) + { + for (route in routes) + { + if (route.coordinates.size > 1) + { + val polylineOptions = PolylineOptions().color( route.color ) + val polyline = googleMap.addPolyline(polylineOptions) + polyline.points = route.coordinates + } + if (!routeIds.contains(route.id)) + { + routeIds.add( route.id ) + } + } + } + + @MainThread + private fun updateBusses( busses: List ) + { + markers.clearMarkers() + for( bus in busses ) + { + // This section is all about refreshing the markers on the google map + val markerOptions = MarkerOptions() + .position(bus.coordinates) + .flat(true) + .alpha(0f) + .title(bus.routeName) + + val marker = googleMap.addMarker(markerOptions) + markers += marker + + // This section is about refreshing the image + val key = getBusImageKey( bus.heading.toString(), bus.name ) + val encodedImage = sharedPreferences?.getString(key, "") ?: "" + + // If we already have the image, no need for a network request + if (encodedImage != "") + { + val bytes = Base64.decode(encodedImage, Base64.DEFAULT) + val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) + val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap) + marker.setIcon(bitmapDescriptor) + marker.alpha = 1f + } + // If we do NOT already have the image, fire off another network request to get it + else + { +// disposables += TransitStream.getBusImagesStream( bus.color!!, bus.heading.toString() ) +// .subscribe() +// var color = "" +// var heading = "" +// val options = mapOf("color" to color, "heading" to heading) +// Flowable.just(StartEvent()) +// .flatMap { +// campusAPI.getBusImages(options) +// .map { list -> NetworkState.Success.BusImages(list) } +// .onErrorReturn { t -> NetworkState.Failure(t) } +// .observeOn(AndroidSchedulers.mainThread()) +// .startWith(NetworkState.InTransit()) +// } +// .logNetworkState("loading bus images in loadBusses()") +// .ofType(NetworkState.Success.BusImages::class.java) +// .subscribe { +// +// } + } + } + } + + private fun getBusImageKey( heading: String, name: String ): String + { + val x: Double = heading.toDouble() + return name + Integer.toString(( x.toInt() + 29 ) / 30 * 30) + } + + override fun onMarkerClick( marker: Marker ): Boolean = false + + interface OnFragmentInteractionListener + { + // TODO: Update argument type and name + fun onFragmentInteraction(uri: Uri) + } + + companion object + { + + fun newInstance(param1: String, param2: String): LiveMapFragment + { + return LiveMapFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java index f1da0cc..11be525 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MainActivity.java @@ -2,14 +2,14 @@ import android.net.Uri; import android.os.Bundle; -import android.support.design.widget.NavigationView; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; +import com.google.android.material.navigation.NavigationView; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import com.google.firebase.analytics.FirebaseAnalytics; @@ -251,7 +251,7 @@ public void onFragmentInteraction(ViewRouteFragment fragment, String title) { FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); Bundle bundle = new Bundle(); bundle.putString(FirebaseAnalytics.Param.ITEM_ID, title); - bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "Route Viewed"); + bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "BusRoute Viewed"); mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle); fragmentTransaction.add(R.id.container, viewRouteFragment).addToBackStack(viewRouteFragment.getClass().getSimpleName()); diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseMessagingService.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseMessagingService.java index edd56cb..5006584 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseMessagingService.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseMessagingService.java @@ -26,26 +26,23 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.RingtoneManager; import android.net.Uri; -import android.support.v4.app.NotificationCompat; +import androidx.core.app.NotificationCompat; import android.util.Log; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; public class MyFirebaseMessagingService extends FirebaseMessagingService { private static final String TAG = "MyAndroidFCMService"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { - //Log data to Log Cat + //Log busRoutes to Log Cat Log.d(TAG, "From: " + remoteMessage.getFrom()); Log.d(TAG, "Notification Message Body: " + remoteMessage.getNotification().getBody()); //create notification diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/PDFFactory.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/PDFFactory.java index 3cc07f2..90da45f 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/PDFFactory.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/PDFFactory.java @@ -14,7 +14,7 @@ public class PDFFactory { private PDF PURPLE44 = new PDF("Purple 44", "purple_44_schedule"); private PDF RED26 = new PDF("Red 26", "red_26_schedule"); private PDF REMOTEEXPRESS = new PDF("Remote Express 48", "remoteexpress_48_schedule"); - private PDF ROUTE13 = new PDF("Route 13", "route_13_schedule"); + private PDF ROUTE13 = new PDF("BusRoute 13", "route_13_schedule"); private PDF TAN35 = new PDF("Tan 35", "tan_35_schedule"); private PDF YELLOW12 = new PDF("Yellow 12", "yellow_12_schedule"); private PDF GRAY21 = new PDF("Gray 21", "gray_21_schedule"); @@ -34,7 +34,7 @@ public class PDFFactory { private PDF PURPLE44_ROUTE = new PDF("Purple 44", "purple_44_route"); private PDF RED26_ROUTE = new PDF("Red 26", "red_26_route"); private PDF REMOTEEXPRESS_ROUTE = new PDF("Remote Express 48", "remoteexpress_48_route"); - private PDF ROUTE13_ROUTE = new PDF("Route 13", "route_13_route"); + private PDF ROUTE13_ROUTE = new PDF("BusRoute 13", "route_13_route"); private PDF TAN35_ROUTE = new PDF("Tan 35", "tan_35_route"); private PDF YELLOW12_ROUTE = new PDF("Yellow 12", "yellow_12_route"); private PDF GRAY21_ROUTE = new PDF("Gray 21", "gray_21_route"); diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java index 985fb0a..cdcaa02 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ParkingMapFragment.java @@ -3,7 +3,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/RoutesFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/RoutesFragment.java index 38fd7d1..3fe2eb1 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/RoutesFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/RoutesFragment.java @@ -2,8 +2,8 @@ import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/SchedulesFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/SchedulesFragment.java index eaefd4a..ff648ad 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/SchedulesFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/SchedulesFragment.java @@ -2,8 +2,8 @@ import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java index 2affcf0..694b097 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewRouteFragment.java @@ -3,7 +3,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java index 6b88edc..a763e71 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/ViewScheduleFragment.java @@ -3,7 +3,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt new file mode 100644 index 0000000..326d55c --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt @@ -0,0 +1,21 @@ +package razorbacktransit.arcu.razorbacktransit.model.bus + +import com.google.android.gms.maps.model.LatLng + +data class Bus(val id: String, + val fleet: String, + val name: String, + val description: String, + val zonarId: String, + val gpsId: String, + val coordinates: LatLng, + val speed: Float, + val heading: Float, + val power: Boolean, + val date: String, + val color: String?, + val routeName: String?, + val routeId: String?, + val distance: Double?, + val nextStop: String?, + val nextArrival: String?) \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt new file mode 100644 index 0000000..6ab4644 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt @@ -0,0 +1,44 @@ +package razorbacktransit.arcu.razorbacktransit.model.bus + +import com.google.android.gms.maps.model.LatLng +import com.squareup.moshi.Json + +data class BusJson(@field:Json(name = "id") val id: String, + @field:Json(name = "fleet") val fleet: String, + @field:Json(name = "name") val name: String, + @field:Json(name = "description") val description: String, + @field:Json(name = "zonarId") val zonarId: String, + @field:Json(name = "gpsId") val gpsId: String, + @field:Json(name = "latitude") val latitude: Double, + @field:Json(name = "longitude") val longitude: Double, + @field:Json(name = "speed") val speed: Float, + @field:Json(name = "heading") val heading: Float, + @field:Json(name = "power") val power: Boolean, + @field:Json(name = "date") val date: String, + @field:Json(name = "color") val color: String?, + @field:Json(name = "routeName") val routeName: String?, + @field:Json(name = "routeId") val routeId: String?, + @field:Json(name = "distance") val distance: Double?, + @field:Json(name = "nextStop") val nextStop: String?, + @field:Json(name = "nextArrival") val nextArrival: String?) +{ + fun toBus() = Bus( + id = id, + fleet = fleet, + name = name, + description = description, + zonarId = zonarId, + gpsId = gpsId, + coordinates = LatLng(latitude, longitude), + speed = speed, + heading = heading, + power = power, + date = date, + color = color, + routeName = routeName, + routeId = routeId, + distance = distance, + nextStop = nextStop, + nextArrival = nextArrival + ) +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJsonAdapter.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJsonAdapter.kt new file mode 100644 index 0000000..4d38954 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJsonAdapter.kt @@ -0,0 +1,8 @@ +package razorbacktransit.arcu.razorbacktransit.model.bus + +import com.squareup.moshi.FromJson + +class BusJsonAdapter +{ + @FromJson fun busFromJson(busJson: BusJson): Bus = busJson.toBus() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt new file mode 100644 index 0000000..d610a4b --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt @@ -0,0 +1,5 @@ +package razorbacktransit.arcu.razorbacktransit.model.busimage + +class BusImage +{ +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt new file mode 100644 index 0000000..6e505ed --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt @@ -0,0 +1,17 @@ +package razorbacktransit.arcu.razorbacktransit.model.route + +import android.graphics.Color +import com.google.android.gms.maps.model.LatLng + +data class Route(val id: String, + val name: String, + val description: String, + val color: Int, + val coordinates: List, + val status: Int, + val inService: Int, + val pdfUrl: String?, + val nextArrival: String?, + val length: Float, + val departureStop: Int, + val nextDeparture: String?) \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt new file mode 100644 index 0000000..22e4ad1 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt @@ -0,0 +1,52 @@ +package razorbacktransit.arcu.razorbacktransit.model.route + +import android.graphics.Color +import androidx.core.graphics.toColor +import com.google.android.gms.maps.model.LatLng +import com.squareup.moshi.Json + +data class RouteJson(@field:Json(name = "id") val id: String, + @field:Json(name = "name") val name: String, + @field:Json(name = "description") val description: String, + @field:Json(name = "color") val color: String, + @field:Json(name = "shape") val shape: String, + @field:Json(name = "status") val status: Int, + @field:Json(name = "inService") val inService: Int, + @field:Json(name = "url") val pdfUrl: String?, + @field:Json(name = "nextArrival") val nextArrival: String?, + @field:Json(name = "length") val length: Float, + @field:Json(name = "departureStop") val departureStop: Int, + @field:Json(name = "nextDeparture") val nextDeparture: String?) +{ + fun toRoute(): Route + { + val coords = parseCoordinates( this.shape ) + return Route( + id = id, + name = name.substring(0, 2), + description = description, + color = Color.parseColor( color ), + coordinates = coords, + status = status, + inService = inService, + pdfUrl = pdfUrl, + nextArrival = nextArrival, + length = length, + departureStop = departureStop, + nextDeparture = nextDeparture + ) + } + + private fun parseCoordinates( shape: String ): List + { + val pairs = shape.split(",") + val list = arrayListOf() + for(pair in pairs) + { + val latLongPair = pair.split(" ") + list.add( LatLng( latLongPair[0].toDouble(), latLongPair[1].toDouble() ) ) + + } + return list + } +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJsonAdapter.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJsonAdapter.kt new file mode 100644 index 0000000..83b8f46 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJsonAdapter.kt @@ -0,0 +1,8 @@ +package razorbacktransit.arcu.razorbacktransit.model.route + +import com.squareup.moshi.FromJson + +class RouteJsonAdapter +{ + @FromJson fun routeFromJson( routeJson: RouteJson ): Route = routeJson.toRoute() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt new file mode 100644 index 0000000..3a5ab06 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt @@ -0,0 +1,5 @@ +package razorbacktransit.arcu.razorbacktransit.model.stopimage + +class StopImage +{ +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt new file mode 100644 index 0000000..d3a035b --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt @@ -0,0 +1,36 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import io.reactivex.Flowable +import okhttp3.ResponseBody +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.stopimage.StopImage +import retrofit2.http.GET +import retrofit2.http.Query +import retrofit2.http.QueryMap + +// Base +// https://campusdata.uark.edu/api/ +interface CampusService +{ + @GET("routes") + fun getRoutes(): Flowable> + + @GET("buses") + fun getAllBuses(): Flowable> + + @GET("busimages") + fun getBusImages(@QueryMap colorAndHeading: Map): Flowable> + + @GET("stops") + fun getStops(@Query("routeIds") routeId: String): Flowable + + @GET("stopimages") + fun getStopImages(@QueryMap options: Map): Flowable> +} +// Buildings +// https://campusdata.uark.edu/api/buildings?callback=Buildings + +// Routes +// https://campusdata.uark.edu/api/routes?callback=Routes \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt new file mode 100644 index 0000000..38c9328 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt @@ -0,0 +1,19 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.stopimage.StopImage + +sealed class NetworkState +{ + class InTransit(val message: String = ""): NetworkState() + sealed class Success: NetworkState() + { + class Routes(val busRoutes: List): Success() + class Busses(val busses: List): Success() + class StopImages(val stopImages: List): Success() + class BusImages(val busImages: List): Success() + } + class Failure(val t: Throwable): NetworkState() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt new file mode 100644 index 0000000..8ea24af --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt @@ -0,0 +1,3 @@ +package razorbacktransit.arcu.razorbacktransit.network + +class StartEvent \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt new file mode 100644 index 0000000..520f7e6 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt @@ -0,0 +1,77 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import io.reactivex.Flowable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter +import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter +import razorbacktransit.arcu.razorbacktransit.utils.logNetworkState +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit + +object TransitStream +{ + private val moshiAdapter = Moshi.Builder() + .add( RouteJsonAdapter() ) + .add( BusJsonAdapter() ) + .add( KotlinJsonAdapterFactory() ) + .build() + + private val campusAPI = Retrofit.Builder() + .baseUrl( "https://campusdata.uark.edu/api/" ) + .addCallAdapterFactory( RxJava2CallAdapterFactory.createWithScheduler( Schedulers.io() ) ) + .addConverterFactory( MoshiConverterFactory.create( moshiAdapter ).asLenient() ) + .build() + .create( CampusService::class.java ) + + val routes: Flowable> = Flowable.just( StartEvent() ) + .flatMap { + campusAPI.getRoutes() + .map { routes: List -> NetworkState.Success.Routes(routes) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn( AndroidSchedulers.mainThread() ) + .startWith( NetworkState.InTransit() ) + } + .logNetworkState("loadRoutes()") + .ofType( NetworkState.Success.Routes::class.java ) + .map { it.busRoutes } + + val buses: Flowable> = Flowable.interval(5, TimeUnit.SECONDS) + .flatMap { + campusAPI.getAllBuses() + .map { busses: List -> NetworkState.Success.Busses(busses) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith( NetworkState.InTransit() ) + } + .logNetworkState("loadBusses()") + .ofType( NetworkState.Success.Busses::class.java ) + .map { it.busses } + .filter { it.isNotEmpty() } + .doOnNext { } + .publish() + .refCount() + + fun getBusImagesStream( color: String, heading: String ): Flowable + { + val map = HashMap() + map["color"] = color + map["heading"] = heading + return Flowable.just( StartEvent() ) + .flatMap { + campusAPI.getBusImages( map ) + .map { routes: List -> NetworkState.Success.BusImages(routes) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn( AndroidSchedulers.mainThread() ) + .startWith( NetworkState.InTransit() ) + } + .map { BusImage() } + } +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt new file mode 100644 index 0000000..87ed292 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt @@ -0,0 +1,32 @@ +package razorbacktransit.arcu.razorbacktransit.utils + +import android.util.Log +import com.google.android.gms.maps.model.Marker +import io.reactivex.Flowable +import razorbacktransit.arcu.razorbacktransit.network.NetworkState + +fun Flowable.logNetworkState( methodName: String ): Flowable +{ + return this.doOnNext { + when(it) + { + is NetworkState.Failure -> + { + Log.d("NETWORKDEBUGGING", "$methodName: FAILURE, ${it.t.localizedMessage}") + it.t.printStackTrace() + } + is NetworkState.InTransit -> Log.d("NETWORKDEBUGGING", "$methodName: IN TRANSIT") + is NetworkState.Success -> Log.d("NETWORKDEBUGGING", "$methodName: SUCCESS") + } + } + +} + +fun ArrayList.clearMarkers() +{ + for(marker in this) + { + marker.remove() + } + this.clear() +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a61d8a6..8e69e22 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - - + diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml index 6517f69..7132181 100644 --- a/app/src/main/res/layout/app_bar_main.xml +++ b/app/src/main/res/layout/app_bar_main.xml @@ -1,26 +1,26 @@ - - - - + - + diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 3f65978..5e8fe3c 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -1,5 +1,5 @@ - - + diff --git a/build.gradle b/build.gradle index c6ab89b..2ef0d5b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.3.10' repositories { jcenter() google() @@ -11,6 +12,7 @@ buildscript { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath 'com.google.gms:google-services:4.0.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. From d52bf17897bcc4db22ea25fc09da27b39d9963c0 Mon Sep 17 00:00:00 2001 From: doperez Date: Mon, 3 Dec 2018 12:34:30 -0600 Subject: [PATCH 3/6] Stream of busses working on the Google Map --- .idea/caches/build_file_checksums.ser | Bin 535 -> 535 bytes app/build.gradle | 5 + .../arcu/razorbacktransit/LiveMapFragment.kt | 127 +++++------------- .../arcu/razorbacktransit/model/bus/Bus.kt | 31 ++++- .../model/busimage/BusImage.kt | 6 +- .../model/busimage/BusImageResponse.kt | 6 + .../model/busimage/BusMarker.kt | 43 ++++++ .../razorbacktransit/model/route/Route.kt | 2 +- .../razorbacktransit/model/route/RouteJson.kt | 2 +- .../razorbacktransit/network/CampusService.kt | 6 +- .../razorbacktransit/network/NetworkState.kt | 7 +- .../razorbacktransit/network/TransitStream.kt | 115 ++++++++++------ 12 files changed, 206 insertions(+), 144 deletions(-) create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 82c9e454f79c273c3f1d82bbc17cab614291bc06..157234bcb8ce275500db3502c1e7300e52a99a8b 100644 GIT binary patch delta 74 zcmV-Q0JZ;@1eXMmm;_VCyVH@J;t;FS3hKL4vxR^;3Epxq0XCD40ZS0Hjb0Ph^@!qK g$W!1&*k!Jh=>Z)PsS|O}fZs%jvlfS?9!*qtcp=^*xc~qF delta 74 zcmV-Q0JZ;@1eXMmm;@zaB>9n?;t=AQJwDty6g*9UPO0%>({_`N0ZR~V>^GE(LbD1X gBe1YZoztZ)PREvlaGYin{Fq!Z+b?#+_c(C3e=>Px# diff --git a/app/build.gradle b/app/build.gradle index c04b74c..95989aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' implementation "com.squareup.retrofit2:converter-moshi:2.4.0" implementation "com.squareup.moshi:moshi-kotlin:1.8.0" + implementation 'com.squareup.picasso:picasso:2.71828' implementation 'io.reactivex.rxjava2:rxjava:2.2.3' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' @@ -58,7 +59,11 @@ dependencies { annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1' implementation 'androidx.core:core-ktx:1.0.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } apply plugin: 'com.google.gms.google-services' apply plugin: 'kotlin-android' +repositories { + mavenCentral() +} diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt index 42e6db5..f2a4839 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt @@ -2,12 +2,8 @@ package razorbacktransit.arcu.razorbacktransit import android.content.Context import android.content.SharedPreferences -import android.graphics.BitmapFactory -import android.graphics.Color import android.net.Uri import android.os.Bundle -import android.util.Base64 -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -17,15 +13,21 @@ import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment -import com.google.android.gms.maps.model.* +import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.Marker +import com.google.android.gms.maps.model.MarkerOptions +import com.google.android.gms.maps.model.PolylineOptions +import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign +import io.reactivex.schedulers.Schedulers import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.network.TransitStream import razorbacktransit.arcu.razorbacktransit.utils.clearMarkers -import java.util.* +import java.util.concurrent.TimeUnit + class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener { @@ -34,16 +36,14 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL private var sharedPreferences: SharedPreferences? = null private var editor: SharedPreferences.Editor? = null - private val stopMarkerHashMap = HashMap() private val markers = arrayListOf() - private val routeIds = arrayListOf() private var stopImageWidth: Int = 0 private var stopImageHeight: Int = 0 - private var busImageWidth: Int = 0 - private var busImageHeight: Int = 0 private val disposables = CompositeDisposable() + private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).startWith(0).publish().refCount() + override fun onCreate(savedInstanceState: Bundle?) { @@ -57,18 +57,18 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL stopImageWidth = (widthPixels * 0.02638888889).toInt() stopImageHeight = (widthPixels * 0.02638888889).toInt() - busImageWidth = (widthPixels * 0.05833333333).toInt() - busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() - // Update the UI for the routes disposables += TransitStream.routes - .map { it.filter { it.name == "green" } } + .flatMapIterable { it } .observeOn( AndroidSchedulers.mainThread() ) .subscribe( this::updateRoutes ) - // Update the UI for the buses - disposables += TransitStream.buses - .map { buses -> buses.filter { bus -> routeIds.contains( bus.id ) } } + disposables += updateBusesNotification + .flatMap { TransitStream.getBusses( context!! ) } + .observeOn( AndroidSchedulers.mainThread() ) + .doOnNext { markers.clearMarkers() } + .observeOn( Schedulers.computation() ) + .flatMapIterable { it } .observeOn( AndroidSchedulers.mainThread() ) .subscribe( this::updateBusses ) } @@ -87,8 +87,9 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL { super.onResume() - if( this::googleMap.isInitialized ) + if (this::googleMap.isInitialized) { + markers.clearMarkers() googleMap.clear() } } @@ -99,7 +100,7 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL editor!!.apply() } - override fun onAttach( context: Context? ) + override fun onAttach(context: Context?) { super.onAttach(context) if (context is OnFragmentInteractionListener) @@ -119,11 +120,11 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL disposables.dispose() } - override fun onMapReady( map: GoogleMap ) + override fun onMapReady(map: GoogleMap) { googleMap = map googleMap.apply { - moveCamera( CameraUpdateFactory.newLatLng( LatLng(36.09, -94.1785) ) ) + moveCamera(CameraUpdateFactory.newLatLng(LatLng(36.09, -94.1785))) moveCamera(CameraUpdateFactory.zoomTo(12.0f)) setMinZoomPreference(10f) setOnMarkerClickListener(this@LiveMapFragment) @@ -133,84 +134,30 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL } @MainThread - private fun updateRoutes( routes: List ) + private fun updateRoutes(route: Route) { - for (route in routes) + if (route.coordinates.size > 1) { - if (route.coordinates.size > 1) - { - val polylineOptions = PolylineOptions().color( route.color ) - val polyline = googleMap.addPolyline(polylineOptions) - polyline.points = route.coordinates - } - if (!routeIds.contains(route.id)) - { - routeIds.add( route.id ) - } + val polylineOptions = PolylineOptions().color(route.color) + val polyline = googleMap.addPolyline(polylineOptions) + polyline.points = route.coordinates } } @MainThread - private fun updateBusses( busses: List ) - { - markers.clearMarkers() - for( bus in busses ) - { - // This section is all about refreshing the markers on the google map - val markerOptions = MarkerOptions() - .position(bus.coordinates) - .flat(true) - .alpha(0f) - .title(bus.routeName) - - val marker = googleMap.addMarker(markerOptions) - markers += marker - - // This section is about refreshing the image - val key = getBusImageKey( bus.heading.toString(), bus.name ) - val encodedImage = sharedPreferences?.getString(key, "") ?: "" - - // If we already have the image, no need for a network request - if (encodedImage != "") - { - val bytes = Base64.decode(encodedImage, Base64.DEFAULT) - val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) - val bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap) - marker.setIcon(bitmapDescriptor) - marker.alpha = 1f - } - // If we do NOT already have the image, fire off another network request to get it - else - { -// disposables += TransitStream.getBusImagesStream( bus.color!!, bus.heading.toString() ) -// .subscribe() -// var color = "" -// var heading = "" -// val options = mapOf("color" to color, "heading" to heading) -// Flowable.just(StartEvent()) -// .flatMap { -// campusAPI.getBusImages(options) -// .map { list -> NetworkState.Success.BusImages(list) } -// .onErrorReturn { t -> NetworkState.Failure(t) } -// .observeOn(AndroidSchedulers.mainThread()) -// .startWith(NetworkState.InTransit()) -// } -// .logNetworkState("loading bus images in loadBusses()") -// .ofType(NetworkState.Success.BusImages::class.java) -// .subscribe { -// -// } - } - } - } - - private fun getBusImageKey( heading: String, name: String ): String + private fun updateBusses(bus: Bus) { - val x: Double = heading.toDouble() - return name + Integer.toString(( x.toInt() + 29 ) / 30 * 30) + // if two markers have the same id but there has been an update, remove the old one and replace it with the new one + val markerOptions: MarkerOptions = MarkerOptions() + .title( bus.routeName ) + .position( bus.coordinates ) + .icon( bus.icon ) + .flat(true) + .alpha(0f) + markers += googleMap.addMarker( markerOptions ).apply { alpha = 1f } } - override fun onMarkerClick( marker: Marker ): Boolean = false + override fun onMarkerClick(marker: Marker): Boolean = false interface OnFragmentInteractionListener { diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt index 326d55c..448a39b 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt @@ -1,6 +1,16 @@ package razorbacktransit.arcu.razorbacktransit.model.bus +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import com.google.android.gms.maps.model.BitmapDescriptor +import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.MarkerOptions +import com.squareup.picasso.Picasso +import com.squareup.picasso.RequestCreator +import com.squareup.picasso.Target +import razorbacktransit.arcu.razorbacktransit.model.busimage.BusMarker +import java.lang.Exception data class Bus(val id: String, val fleet: String, @@ -18,4 +28,23 @@ data class Bus(val id: String, val routeId: String?, val distance: Double?, val nextStop: String?, - val nextArrival: String?) \ No newline at end of file + val nextArrival: String?): Target +{ + override fun onPrepareLoad(placeHolderDrawable: Drawable?) + { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) + { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) + { + icon = BitmapDescriptorFactory.fromBitmap( bitmap ) + } + + @Transient var requestCreator: RequestCreator? = null + @Transient var icon: BitmapDescriptor? = null +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt index d610a4b..90f8dbe 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt @@ -1,5 +1,5 @@ package razorbacktransit.arcu.razorbacktransit.model.busimage -class BusImage -{ -} \ No newline at end of file +import android.graphics.Bitmap + +class BusImage(val image: Bitmap) \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt new file mode 100644 index 0000000..8a7fc3c --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt @@ -0,0 +1,6 @@ +package razorbacktransit.arcu.razorbacktransit.model.busimage + +class BusImageResponse(val image: String) +{ + +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt new file mode 100644 index 0000000..514c7a8 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt @@ -0,0 +1,43 @@ +package razorbacktransit.arcu.razorbacktransit.model.busimage + +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.Marker +import com.google.android.gms.maps.model.MarkerOptions +import com.squareup.picasso.Picasso +import com.squareup.picasso.Target + +class BusMarker( val marker: MarkerOptions ): Target +{ + override fun onPrepareLoad(placeHolderDrawable: Drawable?) + { + + } + + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) + { + + } + + override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) + { + marker.icon( BitmapDescriptorFactory.fromBitmap( bitmap ) ) + } + + override fun hashCode(): Int + { + return marker.hashCode() + } + + override fun equals(other: Any?): Boolean + { + return if(other is BusMarker) + { + val otherMarker = other.marker + return marker == otherMarker + } + else + false + } +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt index 6e505ed..04ec9ca 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt @@ -9,7 +9,7 @@ data class Route(val id: String, val color: Int, val coordinates: List, val status: Int, - val inService: Int, + val inService: Boolean, val pdfUrl: String?, val nextArrival: String?, val length: Float, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt index 22e4ad1..1d65651 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt @@ -28,7 +28,7 @@ data class RouteJson(@field:Json(name = "id") val id: String, color = Color.parseColor( color ), coordinates = coords, status = status, - inService = inService, + inService = inService == 1, pdfUrl = pdfUrl, nextArrival = nextArrival, length = length, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt index d3a035b..e04b360 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt @@ -1,9 +1,9 @@ package razorbacktransit.arcu.razorbacktransit.network import io.reactivex.Flowable +import io.reactivex.Single import okhttp3.ResponseBody import razorbacktransit.arcu.razorbacktransit.model.bus.Bus -import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.model.stopimage.StopImage import retrofit2.http.GET @@ -18,10 +18,10 @@ interface CampusService fun getRoutes(): Flowable> @GET("buses") - fun getAllBuses(): Flowable> + fun getBuses(@Query("routeIds") ids: String ): Flowable> @GET("busimages") - fun getBusImages(@QueryMap colorAndHeading: Map): Flowable> + fun getBusImages(@QueryMap colorAndHeading: Map): Flowable @GET("stops") fun getStops(@Query("routeIds") routeId: String): Flowable diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt index 38c9328..d8618a0 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt @@ -1,9 +1,8 @@ package razorbacktransit.arcu.razorbacktransit.network +import okhttp3.ResponseBody import razorbacktransit.arcu.razorbacktransit.model.bus.Bus -import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.model.stopimage.StopImage sealed class NetworkState { @@ -12,8 +11,8 @@ sealed class NetworkState { class Routes(val busRoutes: List): Success() class Busses(val busses: List): Success() - class StopImages(val stopImages: List): Success() - class BusImages(val busImages: List): Success() + class StopImages(val stopImages: ResponseBody): Success() + class BusImages(val busImages: Bus): Success() } class Failure(val t: Throwable): NetworkState() } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt index 520f7e6..373ba94 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt @@ -1,77 +1,110 @@ package razorbacktransit.arcu.razorbacktransit.network +import android.content.Context +import android.util.Log +import com.google.android.gms.maps.model.BitmapDescriptor +import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import com.squareup.picasso.Picasso import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers +import okhttp3.HttpUrl import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter -import razorbacktransit.arcu.razorbacktransit.model.busimage.BusImage import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter import razorbacktransit.arcu.razorbacktransit.utils.logNetworkState import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.moshi.MoshiConverterFactory -import java.util.concurrent.TimeUnit object TransitStream { private val moshiAdapter = Moshi.Builder() - .add( RouteJsonAdapter() ) - .add( BusJsonAdapter() ) - .add( KotlinJsonAdapterFactory() ) + .add(RouteJsonAdapter()) + .add(BusJsonAdapter()) + .add(KotlinJsonAdapterFactory()) .build() private val campusAPI = Retrofit.Builder() - .baseUrl( "https://campusdata.uark.edu/api/" ) - .addCallAdapterFactory( RxJava2CallAdapterFactory.createWithScheduler( Schedulers.io() ) ) - .addConverterFactory( MoshiConverterFactory.create( moshiAdapter ).asLenient() ) + .baseUrl("https://campusdata.uark.edu/api/") + .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) + .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) .build() - .create( CampusService::class.java ) + .create(CampusService::class.java) - val routes: Flowable> = Flowable.just( StartEvent() ) + val routes: Flowable> = Flowable.just(StartEvent()) .flatMap { campusAPI.getRoutes() .map { routes: List -> NetworkState.Success.Routes(routes) } .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn( AndroidSchedulers.mainThread() ) - .startWith( NetworkState.InTransit() ) - } - .logNetworkState("loadRoutes()") - .ofType( NetworkState.Success.Routes::class.java ) - .map { it.busRoutes } - - val buses: Flowable> = Flowable.interval(5, TimeUnit.SECONDS) - .flatMap { - campusAPI.getAllBuses() - .map { busses: List -> NetworkState.Success.Busses(busses) } - .onErrorReturn { t -> NetworkState.Failure(t) } .observeOn(AndroidSchedulers.mainThread()) - .startWith( NetworkState.InTransit() ) + .startWith(NetworkState.InTransit()) + .logNetworkState("loadRoutes()") } - .logNetworkState("loadBusses()") - .ofType( NetworkState.Success.Busses::class.java ) - .map { it.busses } - .filter { it.isNotEmpty() } - .doOnNext { } - .publish() - .refCount() + .observeOn(Schedulers.computation()) + .ofType(NetworkState.Success.Routes::class.java) + .map { it.busRoutes.filter { bus -> bus.inService } } + .share() - fun getBusImagesStream( color: String, heading: String ): Flowable + fun getBusses(context: Context): Flowable> { - val map = HashMap() - map["color"] = color - map["heading"] = heading - return Flowable.just( StartEvent() ) - .flatMap { - campusAPI.getBusImages( map ) - .map { routes: List -> NetworkState.Success.BusImages(routes) } + val widthPixels = context.resources.displayMetrics.widthPixels + val picasso = Picasso.Builder(context).build() + return routes.observeOn( Schedulers.computation() ) + .map> { it.map { bus -> bus.id } } + .map { ids: List -> + var idString = "" + for (id in ids) + { + idString += "$id-" + } + Log.d("DEBUGGING", "Request String ${idString.substring(0, idString.lastIndex - 1)}") + return@map idString.substring(0, idString.lastIndex - 1) + } + .observeOn( Schedulers.io() ) + // Load the busses + .flatMap { ids: String -> + campusAPI.getBuses(ids) + .map { busses: List -> NetworkState.Success.Busses(busses) } .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn( AndroidSchedulers.mainThread() ) - .startWith( NetworkState.InTransit() ) + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + } + .logNetworkState("getBusses()") + .observeOn( Schedulers.computation() ) + .ofType(NetworkState.Success.Busses::class.java) + .map { it.busses } + .observeOn( Schedulers.io() ) + // Load the images into the busses + .flatMap { busses: List -> + Flowable.fromIterable( busses ) + .map { + it.apply { + Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") + val busImageWidth = (widthPixels * 0.05833333333).toInt() + val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() + Log.d("NETWORKDEBUGGING", getImageUrl( color!!, heading.toString() )) + this.icon = BitmapDescriptorFactory.fromBitmap( picasso.load( getImageUrl( color!!, heading.toString() ) ).resize(busImageWidth, busImageHeight).get() ) + } + } + .toList() + .toFlowable() } - .map { BusImage() } + .share() } + private fun getImageUrl(color: String, heading: String) = HttpUrl.Builder() + .scheme("https") + .host("campusdata.uark.edu") + .addPathSegment("api") + .addPathSegment("busimages") + .addQueryParameter("color", color) + .addQueryParameter("heading", heading) + .build() + .uri() + .toASCIIString() + .replace("%23", "") + .replace("#", "") } \ No newline at end of file From ec7d89418e07ece245b606ae494def9d4ae430b8 Mon Sep 17 00:00:00 2001 From: doperez Date: Tue, 15 Jan 2019 16:41:36 -0600 Subject: [PATCH 4/6] Added disk caching using Glide and removed the flicker between buses updating. --- .idea/caches/build_file_checksums.ser | Bin 535 -> 535 bytes .idea/misc.xml | 12 +- app/build.gradle | 26 +-- .../ExampleInstrumentedTest.java | 2 +- .../arcu/razorbacktransit/LiveMapFragment.kt | 114 ++++++------ .../MyFirebaseInstanceIDService.java | 4 +- .../arcu/razorbacktransit/model/bus/Bus.kt | 27 +-- .../razorbacktransit/model/bus/BusJson.kt | 36 ++-- .../model/busimage/BusImage.kt | 5 - .../model/busimage/BusImageResponse.kt | 6 - .../model/busimage/BusMarker.kt | 43 ----- .../razorbacktransit/model/route/Route.kt | 1 - .../arcu/razorbacktransit/model/stop/Stop.kt | 16 ++ .../razorbacktransit/model/stop/StopJson.kt | 24 +++ .../model/stop/StopJsonAdapter.kt | 8 + .../model/stopimage/StopImage.kt | 5 - .../razorbacktransit/network/CampusService.kt | 11 +- .../network/LiveMapViewModel.kt | 167 ++++++++++++++++++ .../razorbacktransit/network/NetworkState.kt | 2 + .../razorbacktransit/network/TransitStream.kt | 110 ------------ .../arcu/razorbacktransit/utils/Utils.kt | 41 ++++- build.gradle | 4 +- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- 24 files changed, 376 insertions(+), 294 deletions(-) delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJson.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJsonAdapter.kt delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 157234bcb8ce275500db3502c1e7300e52a99a8b..14d7db5d4d89d1e74a8b5d4f971ac5162c02f13b 100644 GIT binary patch delta 74 zcmV-Q0JZ;@1eXMmmjq~2m?V*#;}E9LCXO}{L(%E~4T+!I(xH=%0ZR}x77A34R?Sgc gGNmS7Zz^+>=>Z)Pi|o5Ebfn=FM%n^8p~E)Yc&HE{GXMYp delta 74 zcmV-Q0JZ;@1eXMmmjq{1#=DW5;}EOT3hKL4vxR^;3Epxq0XCD40ZS0Hjb0Ph^@!qK g$W!1&*k!Jh=>Z)PsS|O}fZs%jvlfS?9!*qtcm??)OaK4? diff --git a/.idea/misc.xml b/.idea/misc.xml index e0d5b93..cc04cd3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,7 +5,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 95989aa..3928cde 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,6 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' android { compileSdkVersion 28 @@ -23,43 +25,45 @@ android { // signingConfig signingConfigs.config } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { + def lifecycle_version = "2.0.0" implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.firebase:firebase-core:16.0.5' implementation 'com.google.firebase:firebase-messaging:17.3.4' - androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) testImplementation 'junit:junit:4.12' - implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.github.barteksc:android-pdf-viewer:2.8.1' implementation 'com.google.android.material:material:1.1.0-alpha01' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.google.android.gms:play-services-maps:16.0.0' - implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' - implementation "com.squareup.retrofit2:converter-moshi:2.4.0" - implementation "com.squareup.moshi:moshi-kotlin:1.8.0" + implementation 'com.squareup.retrofit2:converter-moshi:2.4.0' + implementation 'com.squareup.moshi:moshi-kotlin:1.8.0' implementation 'com.squareup.picasso:picasso:2.71828' - implementation 'io.reactivex.rxjava2:rxjava:2.2.3' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.0.0-alpha1' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' - - implementation 'com.jakewharton:butterknife:9.0.0-rc1' - annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1' - + implementation 'com.jakewharton:butterknife:9.0.0' + kapt 'com.jakewharton:butterknife-compiler:9.0.0' implementation 'androidx.core:core-ktx:1.0.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "android.arch.lifecycle:extensions:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation 'com.github.bumptech.glide:glide:4.8.0' + kapt 'com.github.bumptech.glide:compiler:4.8.0' } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/androidTest/java/razorbacktransit/arcu/razorbacktransit/ExampleInstrumentedTest.java b/app/src/androidTest/java/razorbacktransit/arcu/razorbacktransit/ExampleInstrumentedTest.java index 54c8dbd..d09c403 100644 --- a/app/src/androidTest/java/razorbacktransit/arcu/razorbacktransit/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/razorbacktransit/arcu/razorbacktransit/ExampleInstrumentedTest.java @@ -18,7 +18,7 @@ public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { - // Context of the app under test. + // Context of the applivation under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("razorbacktransit.arcu.razorbacktransit", appContext.getPackageName()); diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt index f2a4839..e93b23b 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt @@ -1,14 +1,15 @@ package razorbacktransit.arcu.razorbacktransit import android.content.Context -import android.content.SharedPreferences import android.net.Uri import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.MainThread import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProviders import com.google.android.gms.maps.CameraUpdateFactory import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.OnMapReadyCallback @@ -22,9 +23,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign import io.reactivex.schedulers.Schedulers -import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.network.TransitStream +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop +import razorbacktransit.arcu.razorbacktransit.network.LiveMapViewModel import razorbacktransit.arcu.razorbacktransit.utils.clearMarkers import java.util.concurrent.TimeUnit @@ -32,45 +33,22 @@ import java.util.concurrent.TimeUnit class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener { private var mListener: OnFragmentInteractionListener? = null - private lateinit var googleMap: GoogleMap - private var sharedPreferences: SharedPreferences? = null - private var editor: SharedPreferences.Editor? = null + private var googleMap: GoogleMap? = null - private val markers = arrayListOf() + private var busMarkers = arrayListOf() + private val stopMarkers = arrayListOf() - private var stopImageWidth: Int = 0 - private var stopImageHeight: Int = 0 private val disposables = CompositeDisposable() - private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).startWith(0).publish().refCount() + private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).startWith(0).share() + private lateinit var viewModel: LiveMapViewModel + private val filterList = arrayListOf() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - sharedPreferences = activity!!.getPreferences(Context.MODE_PRIVATE) - editor = sharedPreferences!!.edit() - - val widthPixels = activity!!.resources.displayMetrics.widthPixels - - stopImageWidth = (widthPixels * 0.02638888889).toInt() - stopImageHeight = (widthPixels * 0.02638888889).toInt() - - - disposables += TransitStream.routes - .flatMapIterable { it } - .observeOn( AndroidSchedulers.mainThread() ) - .subscribe( this::updateRoutes ) - - disposables += updateBusesNotification - .flatMap { TransitStream.getBusses( context!! ) } - .observeOn( AndroidSchedulers.mainThread() ) - .doOnNext { markers.clearMarkers() } - .observeOn( Schedulers.computation() ) - .flatMapIterable { it } - .observeOn( AndroidSchedulers.mainThread() ) - .subscribe( this::updateBusses ) + viewModel = ViewModelProviders.of(this).get(LiveMapViewModel::class.java) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? @@ -86,20 +64,41 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL override fun onResume() { super.onResume() + Log.d("DEBUGGING", "onResume()") + + disposables += viewModel.routes + .flatMapIterable { it } + .observeOn( AndroidSchedulers.mainThread() ) + .subscribe( this::updateRoutes ) - if (this::googleMap.isInitialized) + disposables += viewModel.getStops() + .flatMapIterable { it } + .observeOn( AndroidSchedulers.mainThread() ) + .subscribe( this::updateStops ) + + disposables += updateBusesNotification + .flatMap { + viewModel.getBusses() + .observeOn(Schedulers.computation()) + .flatMapIterable { it } + .map { MarkerOptions() + .title( it.routeName ) + .position( it.coordinates ) + .icon( it.icon ) + .flat(true) + .alpha(0f) } + .observeOn(AndroidSchedulers.mainThread()) + .toList().toFlowable() + } + .subscribe( this::updateBusses ) + + if (googleMap != null) { - markers.clearMarkers() - googleMap.clear() + busMarkers.clearMarkers() + googleMap?.clear() } } - override fun onPause() - { - super.onPause() - editor!!.apply() - } - override fun onAttach(context: Context?) { super.onAttach(context) @@ -117,13 +116,13 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL { super.onDetach() mListener = null - disposables.dispose() + disposables.clear() } override fun onMapReady(map: GoogleMap) { googleMap = map - googleMap.apply { + googleMap?.apply { moveCamera(CameraUpdateFactory.newLatLng(LatLng(36.09, -94.1785))) moveCamera(CameraUpdateFactory.zoomTo(12.0f)) setMinZoomPreference(10f) @@ -139,22 +138,35 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL if (route.coordinates.size > 1) { val polylineOptions = PolylineOptions().color(route.color) - val polyline = googleMap.addPolyline(polylineOptions) + val polyline = googleMap!!.addPolyline(polylineOptions) polyline.points = route.coordinates } } @MainThread - private fun updateBusses(bus: Bus) + private fun updateBusses(markers: List) + { + val newMarkers = ArrayList() + for(marker in markers) + { + newMarkers += googleMap!!.addMarker(marker).apply { alpha = 1f } + } + busMarkers.clearMarkers() + busMarkers = newMarkers + } + + @MainThread + private fun updateStops(stop: Stop) { - // if two markers have the same id but there has been an update, remove the old one and replace it with the new one val markerOptions: MarkerOptions = MarkerOptions() - .title( bus.routeName ) - .position( bus.coordinates ) - .icon( bus.icon ) + .snippet( stop.name ) + .title( stop.nextArrival ) + .position( stop.coordinates ) + .icon( stop.icon ) .flat(true) .alpha(0f) - markers += googleMap.addMarker( markerOptions ).apply { alpha = 1f } + + stopMarkers += googleMap!!.addMarker( markerOptions ).apply { alpha = 1f } } override fun onMarkerClick(marker: Marker): Boolean = false diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseInstanceIDService.java b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseInstanceIDService.java index 8c57e42..f38e7f7 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseInstanceIDService.java +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/MyFirebaseInstanceIDService.java @@ -46,7 +46,7 @@ public void onTokenRefresh() { // If you want to send messages to this application instance or // manage this apps subscriptions on the server side, send the - // Instance ID token to your app server. + // Instance ID token to your applivation server. sendRegistrationToServer(refreshedToken); } // [END refresh_token] @@ -60,6 +60,6 @@ public void onTokenRefresh() { * @param token The new token. */ private void sendRegistrationToServer(String token) { - // TODO: Implement this method to send token to your app server. + // TODO: Implement this method to send token to your applivation server. } } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt index 448a39b..6d4777d 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt @@ -1,16 +1,11 @@ package razorbacktransit.arcu.razorbacktransit.model.bus import android.graphics.Bitmap -import android.graphics.drawable.Drawable +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.transition.Transition import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.LatLng -import com.google.android.gms.maps.model.MarkerOptions -import com.squareup.picasso.Picasso -import com.squareup.picasso.RequestCreator -import com.squareup.picasso.Target -import razorbacktransit.arcu.razorbacktransit.model.busimage.BusMarker -import java.lang.Exception data class Bus(val id: String, val fleet: String, @@ -28,23 +23,7 @@ data class Bus(val id: String, val routeId: String?, val distance: Double?, val nextStop: String?, - val nextArrival: String?): Target + val nextArrival: String?) { - override fun onPrepareLoad(placeHolderDrawable: Drawable?) - { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) - { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) - { - icon = BitmapDescriptorFactory.fromBitmap( bitmap ) - } - - @Transient var requestCreator: RequestCreator? = null @Transient var icon: BitmapDescriptor? = null } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt index 6ab4644..89052e3 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt @@ -3,24 +3,24 @@ package razorbacktransit.arcu.razorbacktransit.model.bus import com.google.android.gms.maps.model.LatLng import com.squareup.moshi.Json -data class BusJson(@field:Json(name = "id") val id: String, - @field:Json(name = "fleet") val fleet: String, - @field:Json(name = "name") val name: String, - @field:Json(name = "description") val description: String, - @field:Json(name = "zonarId") val zonarId: String, - @field:Json(name = "gpsId") val gpsId: String, - @field:Json(name = "latitude") val latitude: Double, - @field:Json(name = "longitude") val longitude: Double, - @field:Json(name = "speed") val speed: Float, - @field:Json(name = "heading") val heading: Float, - @field:Json(name = "power") val power: Boolean, - @field:Json(name = "date") val date: String, - @field:Json(name = "color") val color: String?, - @field:Json(name = "routeName") val routeName: String?, - @field:Json(name = "routeId") val routeId: String?, - @field:Json(name = "distance") val distance: Double?, - @field:Json(name = "nextStop") val nextStop: String?, - @field:Json(name = "nextArrival") val nextArrival: String?) +data class BusJson(@Json(name = "id") val id: String, + @Json(name = "fleet") val fleet: String, + @Json(name = "name") val name: String, + @Json(name = "description") val description: String, + @Json(name = "zonarId") val zonarId: String, + @Json(name = "gpsId") val gpsId: String, + @Json(name = "latitude") val latitude: Double, + @Json(name = "longitude") val longitude: Double, + @Json(name = "speed") val speed: Float, + @Json(name = "heading") val heading: Float, + @Json(name = "power") val power: Boolean, + @Json(name = "date") val date: String, + @Json(name = "color") val color: String?, + @Json(name = "routeName") val routeName: String?, + @Json(name = "routeId") val routeId: String?, + @Json(name = "distance") val distance: Double?, + @Json(name = "nextStop") val nextStop: String?, + @Json(name = "nextArrival") val nextArrival: String?) { fun toBus() = Bus( id = id, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt deleted file mode 100644 index 90f8dbe..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImage.kt +++ /dev/null @@ -1,5 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.model.busimage - -import android.graphics.Bitmap - -class BusImage(val image: Bitmap) \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt deleted file mode 100644 index 8a7fc3c..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusImageResponse.kt +++ /dev/null @@ -1,6 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.model.busimage - -class BusImageResponse(val image: String) -{ - -} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt deleted file mode 100644 index 514c7a8..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/busimage/BusMarker.kt +++ /dev/null @@ -1,43 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.model.busimage - -import android.graphics.Bitmap -import android.graphics.drawable.Drawable -import com.google.android.gms.maps.model.BitmapDescriptorFactory -import com.google.android.gms.maps.model.Marker -import com.google.android.gms.maps.model.MarkerOptions -import com.squareup.picasso.Picasso -import com.squareup.picasso.Target - -class BusMarker( val marker: MarkerOptions ): Target -{ - override fun onPrepareLoad(placeHolderDrawable: Drawable?) - { - - } - - override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) - { - - } - - override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) - { - marker.icon( BitmapDescriptorFactory.fromBitmap( bitmap ) ) - } - - override fun hashCode(): Int - { - return marker.hashCode() - } - - override fun equals(other: Any?): Boolean - { - return if(other is BusMarker) - { - val otherMarker = other.marker - return marker == otherMarker - } - else - false - } -} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt index 04ec9ca..1c214f0 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt @@ -1,6 +1,5 @@ package razorbacktransit.arcu.razorbacktransit.model.route -import android.graphics.Color import com.google.android.gms.maps.model.LatLng data class Route(val id: String, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt new file mode 100644 index 0000000..6491001 --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt @@ -0,0 +1,16 @@ +package razorbacktransit.arcu.razorbacktransit.model.stop + +import com.google.android.gms.maps.model.BitmapDescriptor +import com.google.android.gms.maps.model.LatLng + +class Stop(val id: String, + val name: String, + val description: String, + val coordinates: LatLng, + val order: Int, + val distance: String?, + val nextArrival: String?) +{ + @Transient var icon: BitmapDescriptor? = null + @Transient var routeIds: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJson.kt new file mode 100644 index 0000000..21c9fab --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJson.kt @@ -0,0 +1,24 @@ +package razorbacktransit.arcu.razorbacktransit.model.stop + +import com.google.android.gms.maps.model.LatLng +import com.squareup.moshi.Json + +data class StopJson(@Json(name = "id") val id: String, + @Json(name = "name") val name: String, + @Json(name = "description") val description: String, + @Json(name = "latitude") val latitude: String, + @Json(name = "longitude") val longitude: String, + @Json(name = "order") val order: Int, + @Json(name = "distance") val distance: String?, + @Json(name = "nextArrival") val nextArrival: String?) +{ + fun toStop() = Stop( + id = id, + name = name, + description = description, + coordinates = LatLng(latitude.toDouble(), longitude.toDouble()), + order = order, + distance = distance, + nextArrival = nextArrival + ) +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJsonAdapter.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJsonAdapter.kt new file mode 100644 index 0000000..c1c6aba --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/StopJsonAdapter.kt @@ -0,0 +1,8 @@ +package razorbacktransit.arcu.razorbacktransit.model.stop + +import com.squareup.moshi.FromJson + +class StopJsonAdapter +{ + @FromJson fun stopFromJson(stopJson: StopJson): Stop = stopJson.toStop() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt deleted file mode 100644 index 3a5ab06..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stopimage/StopImage.kt +++ /dev/null @@ -1,5 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.model.stopimage - -class StopImage -{ -} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt index e04b360..52b0d76 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/CampusService.kt @@ -1,11 +1,10 @@ package razorbacktransit.arcu.razorbacktransit.network import io.reactivex.Flowable -import io.reactivex.Single import okhttp3.ResponseBody import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.model.stopimage.StopImage +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop import retrofit2.http.GET import retrofit2.http.Query import retrofit2.http.QueryMap @@ -20,14 +19,8 @@ interface CampusService @GET("buses") fun getBuses(@Query("routeIds") ids: String ): Flowable> - @GET("busimages") - fun getBusImages(@QueryMap colorAndHeading: Map): Flowable - @GET("stops") - fun getStops(@Query("routeIds") routeId: String): Flowable - - @GET("stopimages") - fun getStopImages(@QueryMap options: Map): Flowable> + fun getStops(@Query("routeIds") ids: String ): Flowable> } // Buildings // https://campusdata.uark.edu/api/buildings?callback=Buildings diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt new file mode 100644 index 0000000..be9540d --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt @@ -0,0 +1,167 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import android.app.Application +import android.graphics.drawable.Drawable +import android.util.Log +import androidx.core.graphics.drawable.toBitmap +import androidx.lifecycle.AndroidViewModel +import com.bumptech.glide.Glide +import com.bumptech.glide.GlideBuilder +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.Target +import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import com.squareup.picasso.Picasso +import io.reactivex.Flowable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.HttpUrl +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop +import razorbacktransit.arcu.razorbacktransit.model.stop.StopJsonAdapter +import razorbacktransit.arcu.razorbacktransit.utils.buildBusIdsString +import razorbacktransit.arcu.razorbacktransit.utils.buildStopIdsString +import razorbacktransit.arcu.razorbacktransit.utils.logNetworkState +import razorbacktransit.arcu.razorbacktransit.utils.toBitMapDescriptor +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.moshi.MoshiConverterFactory + +class LiveMapViewModel(application: Application): AndroidViewModel(application) +{ + private val applicationContext = application.applicationContext + private val moshiAdapter = Moshi.Builder() + .add(RouteJsonAdapter()) + .add(BusJsonAdapter()) + .add(StopJsonAdapter()) + .add(KotlinJsonAdapterFactory()) + .build() + + private val campusAPI = Retrofit.Builder() + .baseUrl("https://campusdata.uark.edu/api/") + .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) + .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) + .build() + .create(CampusService::class.java) + + val routes: Flowable> = Flowable.just(StartEvent()) + .flatMap { + campusAPI.getRoutes() + .map { routes: List -> NetworkState.Success.Routes(routes) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + .logNetworkState("loadRoutes()") + } + .observeOn(Schedulers.computation()) + .ofType(NetworkState.Success.Routes::class.java) + .map { it.busRoutes.filter { bus -> bus.inService } } + .share() + + fun getBusses(): Flowable> + { + val widthPixels = applicationContext.resources.displayMetrics.widthPixels + return routes.observeOn( Schedulers.computation() ) + .doOnNext { Log.d("NETWORKDEBUGGING", "ATTEMPTING TO BUILD ID'S") } + .buildBusIdsString() + .doOnNext{ Log.d("NETWORKDEBUGGING", it) } + .observeOn( Schedulers.io() ) + // Load the busses + .flatMap { ids: String -> + campusAPI.getBuses(ids) + .map { busses: List -> NetworkState.Success.Busses(busses) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + } + .logNetworkState("getBusses(): Loading from /stops") + .observeOn( Schedulers.computation() ) + .ofType(NetworkState.Success.Busses::class.java) + .map { it.busses } + .observeOn( Schedulers.io() ) + // Load the images into the busses + .flatMap { busses: List -> + Flowable.fromIterable( busses ) + .map { + it.apply { + Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") + val busImageWidth = (widthPixels * 0.05833333333).toInt() + val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() + Log.d("NETWORKDEBUGGING", getBusImageUrl( color!!, heading.toString() )) + this.icon = Glide.with(applicationContext).load(getBusImageUrl(color, heading.toString())).submit().get().toBitMapDescriptor(busImageWidth, busImageHeight) + } + } + .toList() + .toFlowable() + .map { NetworkState.Success.Busses( it ) } + .onErrorReturn { NetworkState.Failure(it) } + .startWith( NetworkState.InTransit() ) + } + .logNetworkState("getStops(): Loading from /stopimages") + .ofType( NetworkState.Success.Busses::class.java ) + .map { it.busses } + .share() + } + + fun getStops(): Flowable> + { + val widthPixels = applicationContext.resources.displayMetrics.widthPixels + return routes.observeOn( Schedulers.computation() ) + .buildStopIdsString() + .observeOn( Schedulers.io() ) + .flatMap { ids: String -> + campusAPI.getStops( ids ) + .map { stops: List -> NetworkState.Success.Stops( stops.onEach { it.routeIds = ids } ) } + .onErrorReturn{ t -> NetworkState.Failure( t ) } + .observeOn( AndroidSchedulers.mainThread() ) + .startWith( NetworkState.InTransit() ) + } + .logNetworkState("getStops()") + .observeOn(Schedulers.computation()) + .ofType(NetworkState.Success.Stops::class.java) + .map { it.stops } + .observeOn( Schedulers.io() ) + .switchMap {stops: List -> + Flowable.fromIterable( stops ) + .map { + it.apply { + val stopImageWidth = (widthPixels * 0.02638888889).toInt() + val stopImageHeight = (widthPixels * 0.02638888889).toInt() + this.icon = Glide.with(applicationContext).load(getStopImageUrl(this.id, this.routeIds!!)).submit().get().toBitMapDescriptor(stopImageWidth, stopImageHeight) + } + } + .toList() + .toFlowable() + } + .share() + } + + private fun getBusImageUrl(color: String, heading: String): String = HttpUrl.Builder() + .scheme("https") + .host("campusdata.uark.edu") + .addPathSegment("api") + .addPathSegment("busimages") + .addQueryParameter("color", color.replace("#", "")) + .addQueryParameter("heading", heading) + .build() + .uri() + .toASCIIString() + + private fun getStopImageUrl( stopId: String, routeIds: String ): String = HttpUrl.Builder() + .scheme("https") + .host("campusdata.uark.edu") + .addPathSegment("api") + .addPathSegment("stopimages") + .addQueryParameter("stopId", stopId) + .addQueryParameter("routeIds", routeIds) + .build() + .uri() + .toASCIIString() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt index d8618a0..abec462 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt @@ -3,6 +3,7 @@ package razorbacktransit.arcu.razorbacktransit.network import okhttp3.ResponseBody import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop sealed class NetworkState { @@ -11,6 +12,7 @@ sealed class NetworkState { class Routes(val busRoutes: List): Success() class Busses(val busses: List): Success() + class Stops(val stops: List): Success() class StopImages(val stopImages: ResponseBody): Success() class BusImages(val busImages: Bus): Success() } diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt deleted file mode 100644 index 373ba94..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/TransitStream.kt +++ /dev/null @@ -1,110 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.network - -import android.content.Context -import android.util.Log -import com.google.android.gms.maps.model.BitmapDescriptor -import com.google.android.gms.maps.model.BitmapDescriptorFactory -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.squareup.picasso.Picasso -import io.reactivex.Flowable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import okhttp3.HttpUrl -import razorbacktransit.arcu.razorbacktransit.model.bus.Bus -import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter -import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter -import razorbacktransit.arcu.razorbacktransit.utils.logNetworkState -import retrofit2.Retrofit -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import retrofit2.converter.moshi.MoshiConverterFactory - -object TransitStream -{ - private val moshiAdapter = Moshi.Builder() - .add(RouteJsonAdapter()) - .add(BusJsonAdapter()) - .add(KotlinJsonAdapterFactory()) - .build() - - private val campusAPI = Retrofit.Builder() - .baseUrl("https://campusdata.uark.edu/api/") - .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) - .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) - .build() - .create(CampusService::class.java) - - val routes: Flowable> = Flowable.just(StartEvent()) - .flatMap { - campusAPI.getRoutes() - .map { routes: List -> NetworkState.Success.Routes(routes) } - .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn(AndroidSchedulers.mainThread()) - .startWith(NetworkState.InTransit()) - .logNetworkState("loadRoutes()") - } - .observeOn(Schedulers.computation()) - .ofType(NetworkState.Success.Routes::class.java) - .map { it.busRoutes.filter { bus -> bus.inService } } - .share() - - fun getBusses(context: Context): Flowable> - { - val widthPixels = context.resources.displayMetrics.widthPixels - val picasso = Picasso.Builder(context).build() - return routes.observeOn( Schedulers.computation() ) - .map> { it.map { bus -> bus.id } } - .map { ids: List -> - var idString = "" - for (id in ids) - { - idString += "$id-" - } - Log.d("DEBUGGING", "Request String ${idString.substring(0, idString.lastIndex - 1)}") - return@map idString.substring(0, idString.lastIndex - 1) - } - .observeOn( Schedulers.io() ) - // Load the busses - .flatMap { ids: String -> - campusAPI.getBuses(ids) - .map { busses: List -> NetworkState.Success.Busses(busses) } - .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn(AndroidSchedulers.mainThread()) - .startWith(NetworkState.InTransit()) - } - .logNetworkState("getBusses()") - .observeOn( Schedulers.computation() ) - .ofType(NetworkState.Success.Busses::class.java) - .map { it.busses } - .observeOn( Schedulers.io() ) - // Load the images into the busses - .flatMap { busses: List -> - Flowable.fromIterable( busses ) - .map { - it.apply { - Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") - val busImageWidth = (widthPixels * 0.05833333333).toInt() - val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() - Log.d("NETWORKDEBUGGING", getImageUrl( color!!, heading.toString() )) - this.icon = BitmapDescriptorFactory.fromBitmap( picasso.load( getImageUrl( color!!, heading.toString() ) ).resize(busImageWidth, busImageHeight).get() ) - } - } - .toList() - .toFlowable() - } - .share() - } - private fun getImageUrl(color: String, heading: String) = HttpUrl.Builder() - .scheme("https") - .host("campusdata.uark.edu") - .addPathSegment("api") - .addPathSegment("busimages") - .addQueryParameter("color", color) - .addQueryParameter("heading", heading) - .build() - .uri() - .toASCIIString() - .replace("%23", "") - .replace("#", "") -} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt index 87ed292..a46f921 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt @@ -1,8 +1,13 @@ package razorbacktransit.arcu.razorbacktransit.utils +import android.graphics.drawable.Drawable import android.util.Log +import androidx.core.graphics.drawable.toBitmap +import com.google.android.gms.maps.model.BitmapDescriptor +import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.Marker import io.reactivex.Flowable +import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.network.NetworkState fun Flowable.logNetworkState( methodName: String ): Flowable @@ -29,4 +34,38 @@ fun ArrayList.clearMarkers() marker.remove() } this.clear() -} \ No newline at end of file +} + +fun Flowable>.buildBusIdsString(): Flowable +{ + return this.map { it.map { route -> route.id } } + .map { ids: List -> + var idString = "" + for (id in ids) + { + idString += "$id-" + } + return@map idString.substring(0, idString.lastIndex - 1) + } +} + +fun Flowable>.buildStopIdsString(): Flowable +{ + return this.map { + it.map { route -> route.id } + } + .filter{ it.isNotEmpty() } + .map { ids: List -> + var idString = "" + for (id in ids) + { + idString += "$id-" + } + return@map idString.substring(0, idString.lastIndex - 1) + } +} + +fun Drawable.toBitMapDescriptor(width: Int, height: Int): BitmapDescriptor +{ + return BitmapDescriptorFactory.fromBitmap(this.toBitmap(width, height)) +} diff --git a/build.gradle b/build.gradle index 2ef0d5b..4dbc07d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.10' + ext.kotlin_version = '1.3.11' repositories { jcenter() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.android.tools.build:gradle:3.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/gradle.properties b/gradle.properties index 9e6fce1..dfd42d6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,8 @@ android.enableJetifier=true android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m +android.jetifier.blacklist = butterknife-compiler + # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 624b7bf..e7b257e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Nov 17 07:14:38 CST 2018 +#Tue Jan 15 00:51:33 CST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip From 5de42ac0a1244d6da336ab4f7f686cd5906eb895 Mon Sep 17 00:00:00 2001 From: doperez Date: Wed, 16 Jan 2019 23:32:43 -0600 Subject: [PATCH 5/6] Began work on route filtering. --- .../arcu/razorbacktransit/LiveMapFragment.kt | 22 +--- .../arcu/razorbacktransit/model/bus/Bus.kt | 6 +- .../razorbacktransit/model/bus/BusJson.kt | 3 +- .../network/LiveMapViewModel.kt | 118 +++++++++++------- .../arcu/razorbacktransit/utils/Utils.kt | 5 +- 5 files changed, 77 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt index e93b23b..df80de9 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt @@ -18,16 +18,13 @@ import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.MarkerOptions import com.google.android.gms.maps.model.PolylineOptions -import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.rxkotlin.plusAssign -import io.reactivex.schedulers.Schedulers import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.model.stop.Stop import razorbacktransit.arcu.razorbacktransit.network.LiveMapViewModel import razorbacktransit.arcu.razorbacktransit.utils.clearMarkers -import java.util.concurrent.TimeUnit class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener @@ -40,10 +37,7 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL private val disposables = CompositeDisposable() - private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).startWith(0).share() private lateinit var viewModel: LiveMapViewModel - private val filterList = arrayListOf() - override fun onCreate(savedInstanceState: Bundle?) { @@ -76,20 +70,8 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL .observeOn( AndroidSchedulers.mainThread() ) .subscribe( this::updateStops ) - disposables += updateBusesNotification - .flatMap { - viewModel.getBusses() - .observeOn(Schedulers.computation()) - .flatMapIterable { it } - .map { MarkerOptions() - .title( it.routeName ) - .position( it.coordinates ) - .icon( it.icon ) - .flat(true) - .alpha(0f) } - .observeOn(AndroidSchedulers.mainThread()) - .toList().toFlowable() - } + disposables += viewModel.getBusses() + .observeOn(AndroidSchedulers.mainThread()) .subscribe( this::updateBusses ) if (googleMap != null) diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt index 6d4777d..5ba052a 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt @@ -1,10 +1,6 @@ package razorbacktransit.arcu.razorbacktransit.model.bus -import android.graphics.Bitmap -import com.bumptech.glide.request.target.SimpleTarget -import com.bumptech.glide.request.transition.Transition import com.google.android.gms.maps.model.BitmapDescriptor -import com.google.android.gms.maps.model.BitmapDescriptorFactory import com.google.android.gms.maps.model.LatLng data class Bus(val id: String, @@ -15,7 +11,7 @@ data class Bus(val id: String, val gpsId: String, val coordinates: LatLng, val speed: Float, - val heading: Float, + val heading: Int, val power: Boolean, val date: String, val color: String?, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt index 89052e3..7b1bbc0 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/BusJson.kt @@ -2,6 +2,7 @@ package razorbacktransit.arcu.razorbacktransit.model.bus import com.google.android.gms.maps.model.LatLng import com.squareup.moshi.Json +import kotlin.math.roundToInt data class BusJson(@Json(name = "id") val id: String, @Json(name = "fleet") val fleet: String, @@ -31,7 +32,7 @@ data class BusJson(@Json(name = "id") val id: String, gpsId = gpsId, coordinates = LatLng(latitude, longitude), speed = speed, - heading = heading, + heading = heading.roundToInt(), power = power, date = date, color = color, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt index be9540d..8ed98b6 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt @@ -1,21 +1,12 @@ package razorbacktransit.arcu.razorbacktransit.network import android.app.Application -import android.graphics.drawable.Drawable import android.util.Log -import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.AndroidViewModel import com.bumptech.glide.Glide -import com.bumptech.glide.GlideBuilder -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.Target -import com.google.android.gms.maps.model.BitmapDescriptorFactory +import com.google.android.gms.maps.model.MarkerOptions import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.squareup.picasso.Picasso import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -33,6 +24,7 @@ import razorbacktransit.arcu.razorbacktransit.utils.toBitMapDescriptor import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit class LiveMapViewModel(application: Application): AndroidViewModel(application) { @@ -50,6 +42,9 @@ class LiveMapViewModel(application: Application): AndroidViewModel(application) .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) .build() .create(CampusService::class.java) + private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).share() + + var enabledList = arrayListOf("221", "223","303", "227") val routes: Flowable> = Flowable.just(StartEvent()) .flatMap { @@ -62,52 +57,79 @@ class LiveMapViewModel(application: Application): AndroidViewModel(application) } .observeOn(Schedulers.computation()) .ofType(NetworkState.Success.Routes::class.java) - .map { it.busRoutes.filter { bus -> bus.inService } } + .flatMap { + Flowable.fromIterable(it.busRoutes) + .filter { route: Route-> + Log.d("DEBUGGINGFILTER", "Id: ${route.id}") + if(route.inService && enabledList.contains(route.id)) + { + Log.d("DEBUGGINGFILTER", "${route.name} permitted!") + } + route.inService && enabledList.contains(route.id) + } + .toList() + .toFlowable() + } .share() - fun getBusses(): Flowable> + fun getBusses(): Flowable> { val widthPixels = applicationContext.resources.displayMetrics.widthPixels - return routes.observeOn( Schedulers.computation() ) - .doOnNext { Log.d("NETWORKDEBUGGING", "ATTEMPTING TO BUILD ID'S") } - .buildBusIdsString() - .doOnNext{ Log.d("NETWORKDEBUGGING", it) } - .observeOn( Schedulers.io() ) - // Load the busses - .flatMap { ids: String -> - campusAPI.getBuses(ids) - .map { busses: List -> NetworkState.Success.Busses(busses) } - .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn(AndroidSchedulers.mainThread()) - .startWith(NetworkState.InTransit()) - } - .logNetworkState("getBusses(): Loading from /stops") - .observeOn( Schedulers.computation() ) - .ofType(NetworkState.Success.Busses::class.java) - .map { it.busses } - .observeOn( Schedulers.io() ) - // Load the images into the busses - .flatMap { busses: List -> - Flowable.fromIterable( busses ) + return updateBusesNotification + .flatMap { + routes.observeOn( Schedulers.computation() ) + .doOnNext { Log.d("NETWORKDEBUGGING", "ATTEMPTING TO BUILD ID'S") } + .buildBusIdsString() + .doOnNext{ Log.d("NETWORKDEBUGGING", it) } + .observeOn( Schedulers.io() ) + // Load the busses + .flatMap { ids: String -> + campusAPI.getBuses(ids) + .map { busses: List -> NetworkState.Success.Busses(busses) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + } + .logNetworkState("getBusses(): Loading from /stops") + .observeOn( Schedulers.computation() ) + .ofType(NetworkState.Success.Busses::class.java) + .map { it.busses } + .observeOn( Schedulers.io() ) + // Load the images into the busses + .flatMap { busses: List -> + Flowable.fromIterable( busses ) + .map { + it.apply { + Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") + val busImageWidth = (widthPixels * 0.05833333333).toInt() + val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() + Log.d("NETWORKDEBUGGING", getBusImageUrl( color!!, heading.toString() )) + this.icon = Glide.with(applicationContext).load(getBusImageUrl(color, heading.toString())).submit().get().toBitMapDescriptor(busImageWidth, busImageHeight) + } + } + .toList() + .toFlowable() + .map { NetworkState.Success.Busses( it ) } + .onErrorReturn { NetworkState.Failure(it) } + .startWith( NetworkState.InTransit() ) + } + .logNetworkState("getStops(): Loading from /stopimages") + .observeOn(Schedulers.computation()) + .ofType( NetworkState.Success.Busses::class.java ) + .map { it.busses } + .flatMapIterable { it } .map { - it.apply { - Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") - val busImageWidth = (widthPixels * 0.05833333333).toInt() - val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() - Log.d("NETWORKDEBUGGING", getBusImageUrl( color!!, heading.toString() )) - this.icon = Glide.with(applicationContext).load(getBusImageUrl(color, heading.toString())).submit().get().toBitMapDescriptor(busImageWidth, busImageHeight) - } + MarkerOptions() + .title( it.routeName ) + .position( it.coordinates ) + .icon( it.icon ) + .flat(true) + .alpha(0f) } - .toList() - .toFlowable() - .map { NetworkState.Success.Busses( it ) } - .onErrorReturn { NetworkState.Failure(it) } - .startWith( NetworkState.InTransit() ) + .toList().toFlowable() } - .logNetworkState("getStops(): Loading from /stopimages") - .ofType( NetworkState.Success.Busses::class.java ) - .map { it.busses } .share() + } fun getStops(): Flowable> diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt index a46f921..3791153 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt @@ -39,6 +39,7 @@ fun ArrayList.clearMarkers() fun Flowable>.buildBusIdsString(): Flowable { return this.map { it.map { route -> route.id } } + .filter{ it.isNotEmpty() } .map { ids: List -> var idString = "" for (id in ids) @@ -51,9 +52,7 @@ fun Flowable>.buildBusIdsString(): Flowable fun Flowable>.buildStopIdsString(): Flowable { - return this.map { - it.map { route -> route.id } - } + return this.map { it.map { route -> route.id } } .filter{ it.isNotEmpty() } .map { ids: List -> var idString = "" From d9dd27eee58fa404b1b66dc9be306cef0712fe59 Mon Sep 17 00:00:00 2001 From: doperez Date: Wed, 13 Mar 2019 11:14:20 -0500 Subject: [PATCH 6/6] Cleaned up RxJava streams using FlowableTransformers, route filtering being prototyped through hard-coding. --- .idea/caches/build_file_checksums.ser | Bin 535 -> 535 bytes app/build.gradle | 18 +- .../arcu/razorbacktransit/LiveMapFragment.kt | 73 +++---- .../arcu/razorbacktransit/model/bus/Bus.kt | 4 +- .../razorbacktransit/model/route/Route.kt | 2 +- .../razorbacktransit/model/route/RouteJson.kt | 5 +- .../arcu/razorbacktransit/model/stop/Stop.kt | 4 +- .../network/LiveMapViewModel.kt | 198 +++--------------- .../razorbacktransit/network/NetworkState.kt | 7 +- .../razorbacktransit/network/NetworkUtils.kt | 64 ++++++ .../network/RouteNetworking.kt | 143 +++++++++++++ .../razorbacktransit/network/StartEvent.kt | 3 - .../razorbacktransit/network/SubmitUiModel.kt | 14 ++ .../arcu/razorbacktransit/utils/Utils.kt | 28 --- build.gradle | 2 +- 15 files changed, 303 insertions(+), 262 deletions(-) create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkUtils.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/RouteNetworking.kt delete mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt create mode 100644 app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/SubmitUiModel.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 14d7db5d4d89d1e74a8b5d4f971ac5162c02f13b..0dcfb7228d66bb4b746f00a8ca829f84f28f3a03 100644 GIT binary patch delta 54 zcmV-60LlNC1eXMmmj!8SAGKDIoZ=8m!nM8N8|BJpE>quT0BFsVjsbWPWJ8jgmzy8Z MguH#yAKSY8c%{)AVE_OC delta 54 zcmV-60LlNC1eXMmmj!53m?YDYoZ=9s&nAvG5kt}G{|$+s+tQ(vjsbWPi|o5Ebfn=F MM%n^8p~E)Yc(orI9RL6T diff --git a/app/build.gradle b/app/build.gradle index 3928cde..d98c658 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ android { buildToolsVersion '28.0.3' defaultConfig { applicationId "razorbacktransit.arcu.razorbacktransit" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 28 versionCode 17 versionName "4.2.1" @@ -42,28 +42,28 @@ dependencies { testImplementation 'junit:junit:4.12' implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'com.github.barteksc:android-pdf-viewer:2.8.1' - implementation 'com.google.android.material:material:1.1.0-alpha01' - implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2' + implementation 'com.google.android.material:material:1.1.0-alpha02' + implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.google.android.gms:play-services-maps:16.0.0' - implementation 'com.squareup.okhttp3:okhttp:3.10.0' - implementation 'com.squareup.retrofit2:retrofit:2.4.0' - implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' + implementation 'com.squareup.okhttp3:okhttp:3.12.0' + implementation 'com.squareup.retrofit2:retrofit:2.5.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.retrofit2:converter-moshi:2.4.0' implementation 'com.squareup.moshi:moshi-kotlin:1.8.0' - implementation 'com.squareup.picasso:picasso:2.71828' - implementation 'io.reactivex.rxjava2:rxjava:2.2.3' + implementation 'io.reactivex.rxjava2:rxjava:2.2.6' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'com.jakewharton.rxbinding3:rxbinding-core:3.0.0-alpha1' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'com.jakewharton:butterknife:9.0.0' kapt 'com.jakewharton:butterknife-compiler:9.0.0' implementation 'androidx.core:core-ktx:1.0.1' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "android.arch.lifecycle:extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation 'com.github.bumptech.glide:glide:4.8.0' kapt 'com.github.bumptech.glide:compiler:4.8.0' + } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt index df80de9..cbbf9e9 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/LiveMapFragment.kt @@ -19,15 +19,15 @@ import com.google.android.gms.maps.model.Marker import com.google.android.gms.maps.model.MarkerOptions import com.google.android.gms.maps.model.PolylineOptions import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.plusAssign +import io.reactivex.disposables.Disposable +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.model.stop.Stop import razorbacktransit.arcu.razorbacktransit.network.LiveMapViewModel +import razorbacktransit.arcu.razorbacktransit.network.SubmitUiModel import razorbacktransit.arcu.razorbacktransit.utils.clearMarkers -class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener +class LiveMapFragment: Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener { private var mListener: OnFragmentInteractionListener? = null private var googleMap: GoogleMap? = null @@ -35,8 +35,7 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL private var busMarkers = arrayListOf() private val stopMarkers = arrayListOf() - private val disposables = CompositeDisposable() - + private lateinit var disposable: Disposable private lateinit var viewModel: LiveMapViewModel override fun onCreate(savedInstanceState: Bundle?) @@ -58,21 +57,17 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL override fun onResume() { super.onResume() - Log.d("DEBUGGING", "onResume()") - - disposables += viewModel.routes - .flatMapIterable { it } - .observeOn( AndroidSchedulers.mainThread() ) - .subscribe( this::updateRoutes ) - - disposables += viewModel.getStops() - .flatMapIterable { it } - .observeOn( AndroidSchedulers.mainThread() ) - .subscribe( this::updateStops ) - disposables += viewModel.getBusses() + disposable = viewModel.observeAll .observeOn(AndroidSchedulers.mainThread()) - .subscribe( this::updateBusses ) + .subscribe { + when(it) + { + is SubmitUiModel.SubmitRoute -> updateRoutes(it.route) + is SubmitUiModel.SubmitBuses -> updateBusses(it.bus) + is SubmitUiModel.SubmitStop -> updateStops(it.stop.icon!!) + } + } if (googleMap != null) { @@ -98,7 +93,7 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL { super.onDetach() mListener = null - disposables.clear() + disposable.dispose() } override fun onMapReady(map: GoogleMap) @@ -115,40 +110,38 @@ class LiveMapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickL } @MainThread - private fun updateRoutes(route: Route) + private fun updateRoutes(routes: List) { - if (route.coordinates.size > 1) + Log.d("DEBUGGING", "updateRoutes()") + for (route in routes) { - val polylineOptions = PolylineOptions().color(route.color) - val polyline = googleMap!!.addPolyline(polylineOptions) - polyline.points = route.coordinates + if (route.coordinates.size > 1) + { + val polylineOptions = PolylineOptions().color(route.color) + val polyline = googleMap!!.addPolyline(polylineOptions) + polyline.points = route.coordinates + } } } @MainThread - private fun updateBusses(markers: List) + private fun updateBusses(buses: List) { - val newMarkers = ArrayList() - for(marker in markers) + Log.d("DEBUGGING", "updateBusses()") + val markers = ArrayList() + for(bus in buses) { - newMarkers += googleMap!!.addMarker(marker).apply { alpha = 1f } + markers += googleMap!!.addMarker(bus.icon).apply { alpha = 1f } } busMarkers.clearMarkers() - busMarkers = newMarkers + busMarkers = markers } @MainThread - private fun updateStops(stop: Stop) + private fun updateStops(stop: MarkerOptions) { - val markerOptions: MarkerOptions = MarkerOptions() - .snippet( stop.name ) - .title( stop.nextArrival ) - .position( stop.coordinates ) - .icon( stop.icon ) - .flat(true) - .alpha(0f) - - stopMarkers += googleMap!!.addMarker( markerOptions ).apply { alpha = 1f } + Log.d("DEBUGGING", "updateStops()") + stopMarkers += googleMap!!.addMarker( stop ).apply { alpha = 1f } } override fun onMarkerClick(marker: Marker): Boolean = false diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt index 5ba052a..21d75f2 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/bus/Bus.kt @@ -2,6 +2,7 @@ package razorbacktransit.arcu.razorbacktransit.model.bus import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.MarkerOptions data class Bus(val id: String, val fleet: String, @@ -21,5 +22,6 @@ data class Bus(val id: String, val nextStop: String?, val nextArrival: String?) { - @Transient var icon: BitmapDescriptor? = null + @Transient var icon: MarkerOptions? = null + @Transient var ids: String? = null } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt index 1c214f0..2ee4aa8 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/Route.kt @@ -2,7 +2,7 @@ package razorbacktransit.arcu.razorbacktransit.model.route import com.google.android.gms.maps.model.LatLng -data class Route(val id: String, +data class Route(val id: Int, val name: String, val description: String, val color: Int, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt index 1d65651..e6a0a57 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/route/RouteJson.kt @@ -1,11 +1,10 @@ package razorbacktransit.arcu.razorbacktransit.model.route import android.graphics.Color -import androidx.core.graphics.toColor import com.google.android.gms.maps.model.LatLng import com.squareup.moshi.Json -data class RouteJson(@field:Json(name = "id") val id: String, +data class RouteJson(@field:Json(name = "id") val id: Int, @field:Json(name = "name") val name: String, @field:Json(name = "description") val description: String, @field:Json(name = "color") val color: String, @@ -23,7 +22,7 @@ data class RouteJson(@field:Json(name = "id") val id: String, val coords = parseCoordinates( this.shape ) return Route( id = id, - name = name.substring(0, 2), + name = name, description = description, color = Color.parseColor( color ), coordinates = coords, diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt index 6491001..b4d46e9 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/model/stop/Stop.kt @@ -1,7 +1,7 @@ package razorbacktransit.arcu.razorbacktransit.model.stop -import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.LatLng +import com.google.android.gms.maps.model.MarkerOptions class Stop(val id: String, val name: String, @@ -11,6 +11,6 @@ class Stop(val id: String, val distance: String?, val nextArrival: String?) { - @Transient var icon: BitmapDescriptor? = null + @Transient var icon: MarkerOptions? = null @Transient var routeIds: String? = null } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt index 8ed98b6..0049805 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/LiveMapViewModel.kt @@ -3,187 +3,47 @@ package razorbacktransit.arcu.razorbacktransit.network import android.app.Application import android.util.Log import androidx.lifecycle.AndroidViewModel -import com.bumptech.glide.Glide -import com.google.android.gms.maps.model.MarkerOptions -import com.squareup.moshi.Moshi -import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import io.reactivex.Flowable -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -import okhttp3.HttpUrl -import razorbacktransit.arcu.razorbacktransit.model.bus.Bus -import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter -import razorbacktransit.arcu.razorbacktransit.model.route.Route -import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter -import razorbacktransit.arcu.razorbacktransit.model.stop.Stop -import razorbacktransit.arcu.razorbacktransit.model.stop.StopJsonAdapter -import razorbacktransit.arcu.razorbacktransit.utils.buildBusIdsString -import razorbacktransit.arcu.razorbacktransit.utils.buildStopIdsString -import razorbacktransit.arcu.razorbacktransit.utils.logNetworkState -import razorbacktransit.arcu.razorbacktransit.utils.toBitMapDescriptor -import retrofit2.Retrofit -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import retrofit2.converter.moshi.MoshiConverterFactory import java.util.concurrent.TimeUnit class LiveMapViewModel(application: Application): AndroidViewModel(application) { private val applicationContext = application.applicationContext - private val moshiAdapter = Moshi.Builder() - .add(RouteJsonAdapter()) - .add(BusJsonAdapter()) - .add(StopJsonAdapter()) - .add(KotlinJsonAdapterFactory()) - .build() - - private val campusAPI = Retrofit.Builder() - .baseUrl("https://campusdata.uark.edu/api/") - .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) - .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) - .build() - .create(CampusService::class.java) - private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()).share() - - var enabledList = arrayListOf("221", "223","303", "227") - - val routes: Flowable> = Flowable.just(StartEvent()) - .flatMap { - campusAPI.getRoutes() - .map { routes: List -> NetworkState.Success.Routes(routes) } - .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn(AndroidSchedulers.mainThread()) - .startWith(NetworkState.InTransit()) - .logNetworkState("loadRoutes()") - } - .observeOn(Schedulers.computation()) - .ofType(NetworkState.Success.Routes::class.java) - .flatMap { - Flowable.fromIterable(it.busRoutes) - .filter { route: Route-> - Log.d("DEBUGGINGFILTER", "Id: ${route.id}") - if(route.inService && enabledList.contains(route.id)) - { - Log.d("DEBUGGINGFILTER", "${route.name} permitted!") - } - route.inService && enabledList.contains(route.id) - } - .toList() - .toFlowable() - } + private val routeNetworking = RouteNetworking(applicationContext) + private val updateBusesNotification = Flowable.interval(5, TimeUnit.SECONDS, Schedulers.io()) + .map { Unit } .share() - fun getBusses(): Flowable> - { - val widthPixels = applicationContext.resources.displayMetrics.widthPixels - return updateBusesNotification - .flatMap { - routes.observeOn( Schedulers.computation() ) - .doOnNext { Log.d("NETWORKDEBUGGING", "ATTEMPTING TO BUILD ID'S") } - .buildBusIdsString() - .doOnNext{ Log.d("NETWORKDEBUGGING", it) } - .observeOn( Schedulers.io() ) - // Load the busses - .flatMap { ids: String -> - campusAPI.getBuses(ids) - .map { busses: List -> NetworkState.Success.Busses(busses) } - .onErrorReturn { t -> NetworkState.Failure(t) } - .observeOn(AndroidSchedulers.mainThread()) - .startWith(NetworkState.InTransit()) - } - .logNetworkState("getBusses(): Loading from /stops") - .observeOn( Schedulers.computation() ) - .ofType(NetworkState.Success.Busses::class.java) - .map { it.busses } - .observeOn( Schedulers.io() ) - // Load the images into the busses - .flatMap { busses: List -> - Flowable.fromIterable( busses ) - .map { - it.apply { - Log.d("NETWORKDEBUGGING", "${it.name}, Color: ${it.color}") - val busImageWidth = (widthPixels * 0.05833333333).toInt() - val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() - Log.d("NETWORKDEBUGGING", getBusImageUrl( color!!, heading.toString() )) - this.icon = Glide.with(applicationContext).load(getBusImageUrl(color, heading.toString())).submit().get().toBitMapDescriptor(busImageWidth, busImageHeight) - } - } - .toList() - .toFlowable() - .map { NetworkState.Success.Busses( it ) } - .onErrorReturn { NetworkState.Failure(it) } - .startWith( NetworkState.InTransit() ) - } - .logNetworkState("getStops(): Loading from /stopimages") - .observeOn(Schedulers.computation()) - .ofType( NetworkState.Success.Busses::class.java ) - .map { it.busses } - .flatMapIterable { it } - .map { - MarkerOptions() - .title( it.routeName ) - .position( it.coordinates ) - .icon( it.icon ) - .flat(true) - .alpha(0f) - } - .toList().toFlowable() + private val routes: Flowable = updateBusesNotification + .compose(routeNetworking.routesEndpoint) + .map { + if(it is NetworkState.Success.Routes) + { + it.busRoutes = it.busRoutes.filter{ route -> listOf(221, 223, 227).contains(route.id) } } - .share() + return@map it + } + .share() - } + private val routesUi: Flowable = routes + .ofType(NetworkState.Success.Routes::class.java) + .map { return@map SubmitUiModel.SubmitRoute(it.busRoutes) } + .distinctUntilChanged() - fun getStops(): Flowable> - { - val widthPixels = applicationContext.resources.displayMetrics.widthPixels - return routes.observeOn( Schedulers.computation() ) - .buildStopIdsString() - .observeOn( Schedulers.io() ) - .flatMap { ids: String -> - campusAPI.getStops( ids ) - .map { stops: List -> NetworkState.Success.Stops( stops.onEach { it.routeIds = ids } ) } - .onErrorReturn{ t -> NetworkState.Failure( t ) } - .observeOn( AndroidSchedulers.mainThread() ) - .startWith( NetworkState.InTransit() ) - } - .logNetworkState("getStops()") - .observeOn(Schedulers.computation()) - .ofType(NetworkState.Success.Stops::class.java) - .map { it.stops } - .observeOn( Schedulers.io() ) - .switchMap {stops: List -> - Flowable.fromIterable( stops ) - .map { - it.apply { - val stopImageWidth = (widthPixels * 0.02638888889).toInt() - val stopImageHeight = (widthPixels * 0.02638888889).toInt() - this.icon = Glide.with(applicationContext).load(getStopImageUrl(this.id, this.routeIds!!)).submit().get().toBitMapDescriptor(stopImageWidth, stopImageHeight) - } - } - .toList() - .toFlowable() - } - .share() - } + private val buses: Flowable = routes + .ofType(NetworkState.Success.Routes::class.java) + .compose(routeNetworking.buses) + .distinctUntilChanged() - private fun getBusImageUrl(color: String, heading: String): String = HttpUrl.Builder() - .scheme("https") - .host("campusdata.uark.edu") - .addPathSegment("api") - .addPathSegment("busimages") - .addQueryParameter("color", color.replace("#", "")) - .addQueryParameter("heading", heading) - .build() - .uri() - .toASCIIString() + private val stops: Flowable = routes + .ofType(NetworkState.Success.Routes::class.java) + .compose(routeNetworking.stops) + .distinctUntilChanged { old, new -> old.stops != new.stops } + .flatMapIterable { it.stops } + .map { SubmitUiModel.SubmitStop(it) } - private fun getStopImageUrl( stopId: String, routeIds: String ): String = HttpUrl.Builder() - .scheme("https") - .host("campusdata.uark.edu") - .addPathSegment("api") - .addPathSegment("stopimages") - .addQueryParameter("stopId", stopId) - .addQueryParameter("routeIds", routeIds) - .build() - .uri() - .toASCIIString() + val observeAll: Flowable = Flowable.merge(routesUi, buses, stops) + .onBackpressureDrop() + .share() } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt index abec462..5a66b33 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkState.kt @@ -1,6 +1,5 @@ package razorbacktransit.arcu.razorbacktransit.network -import okhttp3.ResponseBody import razorbacktransit.arcu.razorbacktransit.model.bus.Bus import razorbacktransit.arcu.razorbacktransit.model.route.Route import razorbacktransit.arcu.razorbacktransit.model.stop.Stop @@ -10,11 +9,9 @@ sealed class NetworkState class InTransit(val message: String = ""): NetworkState() sealed class Success: NetworkState() { - class Routes(val busRoutes: List): Success() - class Busses(val busses: List): Success() + class Routes(var busRoutes: List): Success() + class Buses(val buses: List): Success() class Stops(val stops: List): Success() - class StopImages(val stopImages: ResponseBody): Success() - class BusImages(val busImages: Bus): Success() } class Failure(val t: Throwable): NetworkState() } \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkUtils.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkUtils.kt new file mode 100644 index 0000000..7f589eb --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/NetworkUtils.kt @@ -0,0 +1,64 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import android.util.Log +import io.reactivex.Flowable +import okhttp3.HttpUrl +import razorbacktransit.arcu.razorbacktransit.model.route.Route + +fun getBusImageUrl(color: String, heading: String): String = HttpUrl.Builder() + .scheme("https") + .host("campusdata.uark.edu") + .addPathSegment("api") + .addPathSegment("busimages") + .addQueryParameter("color", color.replace("#", "")) + .addQueryParameter("heading", heading) + .build() + .uri() + .toASCIIString() + +fun getStopImageUrl(stopId: String, routeIds: String): String = HttpUrl.Builder() + .scheme("https") + .host("campusdata.uark.edu") + .addPathSegment("api") + .addPathSegment("stopimages") + .addQueryParameter("stopId", stopId) + .addQueryParameter("routeIds", routeIds) + .build() + .uri() + .toASCIIString() + +fun Flowable>.buildBusIdsString(): Flowable +{ + return this.map { it.map { route -> route.id } } + .filter { it.isNotEmpty() } + .map { ids: List -> + var idString = "" + for (id in ids) + { + idString += "$id-" + } + if (idString[idString.lastIndex] == '-') + { + return@map idString.substring(0, idString.lastIndex) + } + return@map idString + } +} + +fun Flowable>.buildStopIdsString(): Flowable +{ + return this.map { it.map { route -> route.id } } + .filter { it.isNotEmpty() } + .map { ids: List -> + var idString = "" + for (id in ids) + { + idString += "$id-" + } + if (idString[idString.lastIndex] == '-') + { + return@map idString.substring(0, idString.lastIndex) + } + return@map idString + } +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/RouteNetworking.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/RouteNetworking.kt new file mode 100644 index 0000000..9b239da --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/RouteNetworking.kt @@ -0,0 +1,143 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import android.content.Context +import com.bumptech.glide.Glide +import com.google.android.gms.maps.model.MarkerOptions +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import io.reactivex.Flowable +import io.reactivex.FlowableTransformer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.bus.BusJsonAdapter +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.route.RouteJsonAdapter +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop +import razorbacktransit.arcu.razorbacktransit.model.stop.StopJsonAdapter +import razorbacktransit.arcu.razorbacktransit.utils.toBitMapDescriptor +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.moshi.MoshiConverterFactory + +class RouteNetworking(private val applicationContext: Context) +{ + private val widthPixels: Int = applicationContext.resources.displayMetrics.widthPixels + + private val moshiAdapter = Moshi.Builder() + .add(RouteJsonAdapter()) + .add(BusJsonAdapter()) + .add(StopJsonAdapter()) + .add(KotlinJsonAdapterFactory()) + .build() + + private val campusAPI: CampusService = Retrofit.Builder() + .baseUrl("https://campusdata.uark.edu/api/") + .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) + .addConverterFactory(MoshiConverterFactory.create(moshiAdapter).asLenient()) + .build() + .create(CampusService::class.java) + + val routesEndpoint: FlowableTransformer = FlowableTransformer { + it.observeOn(Schedulers.io()) + .flatMap { + campusAPI.getRoutes() + .map { routes: List -> NetworkState.Success.Routes(routes) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + } + } + + val buses: FlowableTransformer = FlowableTransformer { + it.compose(busesEndpoint) + .ofType(NetworkState.Success.Buses::class.java) + .compose(busImagesEndpoint) + } + + private val busesEndpoint: FlowableTransformer = FlowableTransformer { + it.map { it.busRoutes } + .buildBusIdsString() + .flatMap { busIds: String -> + campusAPI.getBuses(busIds) + .map { busses: List -> NetworkState.Success.Buses(busses) } + .onErrorReturn { t -> NetworkState.Failure(t) } + .observeOn(AndroidSchedulers.mainThread()) + .startWith(NetworkState.InTransit()) + } + } + + private val busImagesEndpoint: FlowableTransformer = FlowableTransformer { + it.observeOn( Schedulers.computation() ) + .map { it.buses } + .observeOn( Schedulers.io() ) + // Load the images into the busses + .flatMap { busses: List -> + Flowable.fromIterable( busses ) + .map { + it.apply { + if(it.color != null) + { + val busImageWidth = (widthPixels * 0.05833333333).toInt() + val busImageHeight = (widthPixels.toDouble() * 0.05833333333 * 1.6153846154).toInt() + val myIcon = Glide.with(applicationContext).load(getBusImageUrl(it.color, heading.toString())).submit().get().toBitMapDescriptor(busImageWidth, busImageHeight) + it.icon = MarkerOptions() + .title( it.routeName ) + .position( it.coordinates ) + .icon( myIcon ) + .flat(true) + .alpha(0f) + } + } + } + .observeOn(Schedulers.computation()) + .toList().toFlowable() + .map { buses -> return@map SubmitUiModel.SubmitBuses(buses) } + } + } + + val stops: FlowableTransformer = FlowableTransformer { + it.compose(loadStops) + .ofType(NetworkState.Success.Stops::class.java) + .compose(stopImages) + } + + private val loadStops: FlowableTransformer = FlowableTransformer { + it.map { it.busRoutes } + .buildStopIdsString() + .observeOn( Schedulers.io() ) + .flatMap { ids: String -> + campusAPI.getStops( ids ) + .map { stops: List -> NetworkState.Success.Stops( stops.onEach { it.routeIds = ids } ) } + .onErrorReturn{ t -> NetworkState.Failure( t ) } + .observeOn( AndroidSchedulers.mainThread() ) + .startWith( NetworkState.InTransit() ) + } + } + + private val stopImages: FlowableTransformer = FlowableTransformer { + it.observeOn(Schedulers.computation()) + .map { it.stops } + .observeOn( Schedulers.computation() ) + .switchMap {stops: List -> + Flowable.fromIterable( stops ) + .map { + val stopImageWidth = (widthPixels * 0.02638888889).toInt() + val stopImageHeight = (widthPixels * 0.02638888889).toInt() + val myIcon = Glide.with(applicationContext).load(getStopImageUrl(it.id, it.routeIds!!)).submit().get().toBitMapDescriptor(stopImageWidth, stopImageHeight) + it.icon = MarkerOptions() + .snippet( it.name ) + .title( it.nextArrival ) + .position( it.coordinates ) + .icon( myIcon ) + .flat(true) + .alpha(0f) + return@map it + } + .toList() + .toFlowable() + .map { return@map SubmitUiModel.SubmitStops(it) } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt deleted file mode 100644 index 8ea24af..0000000 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/StartEvent.kt +++ /dev/null @@ -1,3 +0,0 @@ -package razorbacktransit.arcu.razorbacktransit.network - -class StartEvent \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/SubmitUiModel.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/SubmitUiModel.kt new file mode 100644 index 0000000..fc9d71d --- /dev/null +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/network/SubmitUiModel.kt @@ -0,0 +1,14 @@ +package razorbacktransit.arcu.razorbacktransit.network + +import razorbacktransit.arcu.razorbacktransit.model.bus.Bus +import razorbacktransit.arcu.razorbacktransit.model.route.Route +import razorbacktransit.arcu.razorbacktransit.model.stop.Stop + +sealed class SubmitUiModel +{ + class SubmitBuses(val bus: List): SubmitUiModel() + class SubmitStops(val stops: List): SubmitUiModel() + class SubmitStop(val stop: Stop): SubmitUiModel() + class SubmitRoute(val route: List): SubmitUiModel() + class InProgress(): SubmitUiModel() +} \ No newline at end of file diff --git a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt index 3791153..c4c4bdc 100644 --- a/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt +++ b/app/src/main/java/razorbacktransit/arcu/razorbacktransit/utils/Utils.kt @@ -36,34 +36,6 @@ fun ArrayList.clearMarkers() this.clear() } -fun Flowable>.buildBusIdsString(): Flowable -{ - return this.map { it.map { route -> route.id } } - .filter{ it.isNotEmpty() } - .map { ids: List -> - var idString = "" - for (id in ids) - { - idString += "$id-" - } - return@map idString.substring(0, idString.lastIndex - 1) - } -} - -fun Flowable>.buildStopIdsString(): Flowable -{ - return this.map { it.map { route -> route.id } } - .filter{ it.isNotEmpty() } - .map { ids: List -> - var idString = "" - for (id in ids) - { - idString += "$id-" - } - return@map idString.substring(0, idString.lastIndex - 1) - } -} - fun Drawable.toBitMapDescriptor(width: Int, height: Int): BitmapDescriptor { return BitmapDescriptorFactory.fromBitmap(this.toBitmap(width, height)) diff --git a/build.gradle b/build.gradle index 4dbc07d..870de58 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files