add openhack files
This commit is contained in:
@ -0,0 +1,489 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
using Android.OS;
|
||||
using Android.Support.V4.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using MyDriving.Droid.Services;
|
||||
using MyDriving.ViewModel;
|
||||
using MvvmHelpers;
|
||||
using MyDriving.DataObjects;
|
||||
using Android.Gms.Maps;
|
||||
using Android.Gms.Maps.Model;
|
||||
using System;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Plugin.Permissions;
|
||||
using Plugin.Permissions.Abstractions;
|
||||
using System.Threading.Tasks;
|
||||
using MyDriving.Droid.Activities;
|
||||
using MyDriving.Utils;
|
||||
using MyDriving.Droid.Helpers;
|
||||
using Android.Support.Design.Widget;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections.Generic;
|
||||
using Android.Views.Animations;
|
||||
using Android.Animation;
|
||||
|
||||
|
||||
namespace MyDriving.Droid.Fragments
|
||||
{
|
||||
public class FragmentCurrentTrip : Android.Support.V4.App.Fragment, IOnMapReadyCallback
|
||||
{
|
||||
List<LatLng> allPoints;
|
||||
Marker carMarker;
|
||||
TextView distance, distanceUnits, time, load, consumption, consumptionUnits;
|
||||
Polyline driveLine;
|
||||
Color? driveLineColor;
|
||||
FloatingActionButton fab;
|
||||
GoogleMap map;
|
||||
MapView mapView;
|
||||
bool setZoom = true;
|
||||
LinearLayout stats;
|
||||
|
||||
ObservableRangeCollection<TripPoint> trailPointList;
|
||||
CurrentTripViewModel viewModel;
|
||||
|
||||
public void OnMapReady(GoogleMap googleMap)
|
||||
{
|
||||
map = googleMap;
|
||||
if (viewModel != null)
|
||||
SetupMap();
|
||||
}
|
||||
|
||||
public static FragmentCurrentTrip NewInstance() => new FragmentCurrentTrip {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
HasOptionsMenu = true;
|
||||
var view = inflater.Inflate(Resource.Layout.fragment_current_trip, null);
|
||||
|
||||
mapView = view.FindViewById<MapView>(Resource.Id.map);
|
||||
mapView.OnCreate(savedInstanceState);
|
||||
|
||||
fab = view.FindViewById<FloatingActionButton>(Resource.Id.fab);
|
||||
time = view.FindViewById<TextView>(Resource.Id.text_time);
|
||||
distance = view.FindViewById<TextView>(Resource.Id.text_distance);
|
||||
distanceUnits = view.FindViewById<TextView>(Resource.Id.text_distance_units);
|
||||
consumption = view.FindViewById<TextView>(Resource.Id.text_consumption);
|
||||
consumptionUnits = view.FindViewById<TextView>(Resource.Id.text_consumption_units);
|
||||
load = view.FindViewById<TextView>(Resource.Id.text_load);
|
||||
stats = view.FindViewById<LinearLayout>(Resource.Id.stats);
|
||||
stats.Visibility = ViewStates.Invisible;
|
||||
return view;
|
||||
}
|
||||
|
||||
public override void OnActivityCreated(Bundle savedInstanceState)
|
||||
{
|
||||
mapView.GetMapAsync(this);
|
||||
base.OnActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
|
||||
void StartFadeAnimation(bool fadeIn)
|
||||
{
|
||||
//handle first run
|
||||
if (!viewModel.IsRecording && stats.Visibility == ViewStates.Invisible)
|
||||
return;
|
||||
|
||||
var start = fadeIn ? 0f : 1f;
|
||||
var end = fadeIn ? 1f : 0f;
|
||||
stats.Alpha = fadeIn ? 0f : 1f;
|
||||
stats.Visibility = ViewStates.Visible;
|
||||
|
||||
|
||||
var timerAnimator = ValueAnimator.OfFloat(start, end);
|
||||
timerAnimator.SetDuration(Java.Util.Concurrent.TimeUnit.Seconds.ToMillis(1));
|
||||
timerAnimator.SetInterpolator(new AccelerateInterpolator());
|
||||
timerAnimator.Update +=
|
||||
(sender, e) => { Activity.RunOnUiThread(() => stats.Alpha = (float) e.Animation.AnimatedValue); };
|
||||
timerAnimator.Start();
|
||||
}
|
||||
|
||||
void OnLocationServiceConnected(object sender, ServiceConnectedEventArgs e)
|
||||
{
|
||||
viewModel = GeolocationHelper.Current.LocationService.ViewModel;
|
||||
viewModel.PropertyChanged += OnPropertyChanged;
|
||||
ResetTrip();
|
||||
}
|
||||
|
||||
void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(viewModel.CurrentPosition):
|
||||
var latlng = viewModel.CurrentPosition.ToLatLng();
|
||||
UpdateCar(latlng);
|
||||
UpdateCamera(latlng);
|
||||
break;
|
||||
case nameof(viewModel.CurrentTrip):
|
||||
TripSummaryActivity.ViewModel = viewModel.TripSummary;
|
||||
ResetTrip();
|
||||
StartActivity(new Android.Content.Intent(Activity, typeof (TripSummaryActivity)));
|
||||
break;
|
||||
case "Stats":
|
||||
UpdateStats();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateStats()
|
||||
{
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
time.Text = viewModel.ElapsedTime;
|
||||
consumption.Text = viewModel.FuelConsumption;
|
||||
consumptionUnits.Text = viewModel.FuelConsumptionUnits;
|
||||
load.Text = viewModel.EngineLoad;
|
||||
distanceUnits.Text = viewModel.DistanceUnits;
|
||||
distance.Text = viewModel.CurrentTrip.TotalDistanceNoUnits;
|
||||
});
|
||||
}
|
||||
|
||||
void ResetTrip()
|
||||
{
|
||||
trailPointList = viewModel.CurrentTrip.Points as ObservableRangeCollection<TripPoint>;
|
||||
trailPointList.CollectionChanged += OnTrailUpdated;
|
||||
carMarker = null;
|
||||
map?.Clear();
|
||||
allPoints?.Clear();
|
||||
allPoints = null;
|
||||
SetupMap();
|
||||
UpdateStats();
|
||||
StartFadeAnimation(viewModel.IsRecording);
|
||||
Activity.SupportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
|
||||
void AddStartMarker(LatLng start)
|
||||
{
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
var logicalDensity = Resources.DisplayMetrics.Density;
|
||||
var thicknessPoints = (int) Math.Ceiling(20*logicalDensity + .5f);
|
||||
|
||||
var b = ContextCompat.GetDrawable(Activity, Resource.Drawable.ic_start_point) as BitmapDrawable;
|
||||
var finalIcon = Bitmap.CreateScaledBitmap(b.Bitmap, thicknessPoints, thicknessPoints, false);
|
||||
|
||||
var startMarker = new MarkerOptions();
|
||||
startMarker.SetPosition(start);
|
||||
startMarker.SetIcon(BitmapDescriptorFactory.FromBitmap(finalIcon));
|
||||
startMarker.Anchor(.5f, .5f);
|
||||
map.AddMarker(startMarker);
|
||||
});
|
||||
}
|
||||
|
||||
void AddEndMarker(LatLng end)
|
||||
{
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
var logicalDensity = Resources.DisplayMetrics.Density;
|
||||
var thicknessPoints = (int) Math.Ceiling(20*logicalDensity + .5f);
|
||||
var b = ContextCompat.GetDrawable(Activity, Resource.Drawable.ic_end_point) as BitmapDrawable;
|
||||
var finalIcon = Bitmap.CreateScaledBitmap(b.Bitmap, thicknessPoints, thicknessPoints, false);
|
||||
|
||||
var endMarker = new MarkerOptions();
|
||||
endMarker.SetPosition(end);
|
||||
endMarker.SetIcon(BitmapDescriptorFactory.FromBitmap(finalIcon));
|
||||
endMarker.Anchor(.5f, .5f);
|
||||
|
||||
map.AddMarker(endMarker);
|
||||
});
|
||||
}
|
||||
|
||||
void OnTrailUpdated(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
var item = viewModel.CurrentTrip.Points[viewModel.CurrentTrip.Points.Count - 1];
|
||||
if (carMarker != null)
|
||||
UpdateMap(item);
|
||||
else
|
||||
SetupMap();
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateCarIcon(bool recording)
|
||||
{
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
var logicalDensity = Resources.DisplayMetrics.Density;
|
||||
var thicknessCar = (int) Math.Ceiling(26*logicalDensity + .5f);
|
||||
var b =
|
||||
ContextCompat.GetDrawable(Activity,
|
||||
recording ? Resource.Drawable.ic_car_red : Resource.Drawable.ic_car_blue) as BitmapDrawable;
|
||||
//var b = ContextCompat.GetDrawable(Activity, Resource.Drawable.ic_car) as BitmapDrawable;
|
||||
|
||||
var finalIcon = Bitmap.CreateScaledBitmap(b.Bitmap, thicknessCar, thicknessCar, false);
|
||||
|
||||
carMarker?.SetIcon(BitmapDescriptorFactory.FromBitmap(finalIcon));
|
||||
|
||||
fab.SetImageResource(recording ? Resource.Drawable.ic_stop : Resource.Drawable.ic_start);
|
||||
});
|
||||
}
|
||||
|
||||
void SetupMap()
|
||||
{
|
||||
if (map == null)
|
||||
return;
|
||||
|
||||
if (mapView.Width == 0)
|
||||
{
|
||||
mapView.PostDelayed(SetupMap, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewModel.CurrentTrip.HasSimulatedOBDData)
|
||||
{
|
||||
var activity = (BaseActivity) Activity;
|
||||
activity.SupportActionBar.Title = "Current Trip (Sim OBD)";
|
||||
}
|
||||
|
||||
|
||||
TripPoint start = null;
|
||||
if (viewModel.CurrentTrip.Points.Count != 0)
|
||||
start = viewModel.CurrentTrip.Points[0];
|
||||
|
||||
UpdateMap(start, false);
|
||||
|
||||
if (start != null)
|
||||
{
|
||||
UpdateCamera(carMarker.Position);
|
||||
AddStartMarker(start.ToLatLng());
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMap(TripPoint point, bool updateCamera = true)
|
||||
{
|
||||
if (map == null)
|
||||
return;
|
||||
//Get trail position or current potion to move car
|
||||
var latlng = point == null
|
||||
? viewModel?.CurrentPosition?.ToLatLng()
|
||||
: point.ToLatLng();
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
UpdateCar(latlng);
|
||||
driveLine?.Remove();
|
||||
var polyOptions = new PolylineOptions();
|
||||
|
||||
if (allPoints == null)
|
||||
{
|
||||
allPoints = viewModel.CurrentTrip.Points.ToLatLngs();
|
||||
}
|
||||
else if (point != null)
|
||||
{
|
||||
allPoints.Add(point.ToLatLng());
|
||||
}
|
||||
|
||||
polyOptions.Add(allPoints.ToArray());
|
||||
|
||||
if (!driveLineColor.HasValue)
|
||||
driveLineColor = new Color(ContextCompat.GetColor(Activity, Resource.Color.recording_accent));
|
||||
|
||||
polyOptions.InvokeColor(driveLineColor.Value);
|
||||
driveLine = map.AddPolyline(polyOptions);
|
||||
if (updateCamera)
|
||||
UpdateCamera(latlng);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateCar(LatLng latlng)
|
||||
{
|
||||
if (latlng == null || map == null)
|
||||
return;
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
if (carMarker == null)
|
||||
{
|
||||
var car = new MarkerOptions();
|
||||
car.SetPosition(latlng);
|
||||
car.Anchor(.5f, .5f);
|
||||
carMarker = map.AddMarker(car);
|
||||
UpdateCarIcon(viewModel.IsRecording);
|
||||
return;
|
||||
}
|
||||
carMarker.Position = latlng;
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateCamera(LatLng latlng)
|
||||
{
|
||||
if (map == null)
|
||||
return;
|
||||
Activity?.RunOnUiThread(() =>
|
||||
{
|
||||
if (setZoom)
|
||||
{
|
||||
map.MoveCamera(CameraUpdateFactory.NewLatLngZoom(latlng, 14));
|
||||
setZoom = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
map.MoveCamera(CameraUpdateFactory.NewLatLng(latlng));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
|
||||
GeolocationHelper.Current.LocationServiceConnected -= OnLocationServiceConnected;
|
||||
if (viewModel != null)
|
||||
viewModel.PropertyChanged -= OnPropertyChanged;
|
||||
if (trailPointList != null)
|
||||
trailPointList.CollectionChanged -= OnTrailUpdated;
|
||||
if (fab != null)
|
||||
fab.Click -= OnRecordButtonClick;
|
||||
//If we are recording then don't stop the background service
|
||||
if ((viewModel?.IsRecording).GetValueOrDefault())
|
||||
return;
|
||||
|
||||
GeolocationHelper.Current.LocationService.StopLocationUpdates();
|
||||
GeolocationHelper.StopLocationService();
|
||||
}
|
||||
|
||||
public override async void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
GeolocationHelper.Current.LocationServiceConnected += OnLocationServiceConnected;
|
||||
|
||||
if (fab != null)
|
||||
fab.Click += OnRecordButtonClick;
|
||||
await StartLocationService();
|
||||
}
|
||||
|
||||
async Task StartLocationService()
|
||||
{
|
||||
try
|
||||
{
|
||||
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Location);
|
||||
if (status != PermissionStatus.Granted)
|
||||
{
|
||||
var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Location);
|
||||
status = results[Permission.Location];
|
||||
}
|
||||
|
||||
if (status == PermissionStatus.Granted)
|
||||
{
|
||||
if ((viewModel == null || !viewModel.IsRecording) && !GeolocationHelper.Current.IsRunning)
|
||||
await GeolocationHelper.StartLocationService();
|
||||
else
|
||||
OnLocationServiceConnected(null, null);
|
||||
}
|
||||
else if (status != PermissionStatus.Unknown)
|
||||
{
|
||||
Toast.MakeText(Activity, "Location permission is not granted, can't track location",
|
||||
ToastLength.Long);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Instance.Report(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region Options Menu & User Actions
|
||||
|
||||
public override void OnCreateOptionsMenu(IMenu menu, MenuInflater inflater)
|
||||
{
|
||||
//if((viewModel?.IsRecording).GetValueOrDefault())
|
||||
// inflater.Inflate(Resource.Menu.menu_current_trip, menu);
|
||||
base.OnCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Resource.Id.menu_take_photo:
|
||||
if (!(viewModel?.IsBusy).GetValueOrDefault())
|
||||
viewModel?.TakePhotoCommand.Execute(null);
|
||||
break;
|
||||
}
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
async void OnRecordButtonClick(object sender, EventArgs e)
|
||||
{
|
||||
if (viewModel?.CurrentPosition == null || viewModel.IsBusy)
|
||||
return;
|
||||
|
||||
if (viewModel.NeedSave)
|
||||
{
|
||||
await viewModel.SaveRecordingTripAsync();
|
||||
}
|
||||
|
||||
if (viewModel.IsRecording)
|
||||
{
|
||||
if (!await viewModel.StopRecordingTrip())
|
||||
return;
|
||||
|
||||
AddEndMarker(viewModel.CurrentPosition.ToLatLng());
|
||||
UpdateCarIcon(false);
|
||||
|
||||
var activity = (BaseActivity) Activity;
|
||||
activity.SupportActionBar.Title = "Current Trip";
|
||||
|
||||
await viewModel.SaveRecordingTripAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!await viewModel.StartRecordingTrip())
|
||||
return;
|
||||
AddStartMarker(viewModel.CurrentPosition.ToLatLng());
|
||||
|
||||
Activity.SupportInvalidateOptionsMenu();
|
||||
UpdateCarIcon(true);
|
||||
UpdateStats();
|
||||
StartFadeAnimation(true);
|
||||
|
||||
if (viewModel.CurrentTrip.HasSimulatedOBDData)
|
||||
{
|
||||
var activity = (BaseActivity) Activity;
|
||||
activity.SupportActionBar.Title = "Current Trip (Sim OBD)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MapView Lifecycle Events
|
||||
|
||||
public override void OnResume()
|
||||
{
|
||||
base.OnResume();
|
||||
mapView?.OnResume();
|
||||
}
|
||||
|
||||
public override void OnPause()
|
||||
{
|
||||
base.OnPause();
|
||||
mapView?.OnPause();
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
mapView?.OnDestroy();
|
||||
}
|
||||
|
||||
public override void OnSaveInstanceState(Bundle outState)
|
||||
{
|
||||
base.OnSaveInstanceState(outState);
|
||||
mapView?.OnSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
public override void OnLowMemory()
|
||||
{
|
||||
base.OnLowMemory();
|
||||
mapView?.OnLowMemory();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
using Android.OS;
|
||||
using Fragment = Android.Support.V4.App.Fragment;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Android.Content;
|
||||
using MyDriving.Droid.Activities;
|
||||
|
||||
namespace MyDriving.Droid.Fragments
|
||||
{
|
||||
public class FragmentGettingStarted1 : Fragment
|
||||
{
|
||||
public static FragmentGettingStarted1 NewInstance() => new FragmentGettingStarted1 {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
return inflater.Inflate(Resource.Layout.fragment_started_1, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class FragmentGettingStarted2 : Fragment
|
||||
{
|
||||
public static FragmentGettingStarted2 NewInstance() => new FragmentGettingStarted2 {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
return inflater.Inflate(Resource.Layout.fragment_started_2, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class FragmentGettingStarted3 : Fragment
|
||||
{
|
||||
public static FragmentGettingStarted3 NewInstance() => new FragmentGettingStarted3 {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
return inflater.Inflate(Resource.Layout.fragment_started_3, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class FragmentGettingStarted4 : Fragment
|
||||
{
|
||||
public static FragmentGettingStarted4 NewInstance() => new FragmentGettingStarted4 {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
return inflater.Inflate(Resource.Layout.fragment_started_4, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class FragmentGettingStarted5 : Fragment
|
||||
{
|
||||
public static FragmentGettingStarted5 NewInstance() => new FragmentGettingStarted5 {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
var view = inflater.Inflate(Resource.Layout.fragment_started_5, null);
|
||||
|
||||
|
||||
view.FindViewById<Button>(Resource.Id.button_close).Click += (sender, args) =>
|
||||
{
|
||||
var intent = new Intent(Activity, typeof (LoginActivity));
|
||||
intent.AddFlags(ActivityFlags.ClearTop);
|
||||
Activity.StartActivity(intent);
|
||||
Activity.Finish();
|
||||
};
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
using Android.OS;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Views;
|
||||
using Android.Support.V7.Widget;
|
||||
using Android.Support.V4.Widget;
|
||||
using Android.Widget;
|
||||
using MyDriving.ViewModel;
|
||||
using System;
|
||||
using MyDriving.Droid.Activities;
|
||||
using Android.Content;
|
||||
|
||||
|
||||
namespace MyDriving.Droid.Fragments
|
||||
{
|
||||
public class FragmentPastTrips : Fragment
|
||||
{
|
||||
TripAdapter adapter;
|
||||
LinearLayoutManager layoutManager;
|
||||
|
||||
RecyclerView recyclerView;
|
||||
SwipeRefreshLayout refresher;
|
||||
PastTripsViewModel viewModel;
|
||||
|
||||
public static FragmentPastTrips NewInstance() => new FragmentPastTrips {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
var view = inflater.Inflate(Resource.Layout.fragment_past_trips, null);
|
||||
|
||||
viewModel = new PastTripsViewModel();
|
||||
|
||||
recyclerView = view.FindViewById<RecyclerView>(Resource.Id.recyclerView);
|
||||
refresher = view.FindViewById<SwipeRefreshLayout>(Resource.Id.refresher);
|
||||
|
||||
refresher.Refresh += (sender, e) => viewModel.LoadPastTripsCommand.Execute(null);
|
||||
|
||||
|
||||
adapter = new TripAdapter(Activity, viewModel);
|
||||
adapter.ItemClick += OnItemClick;
|
||||
adapter.ItemLongClick += OnItemLongClick;
|
||||
layoutManager = new LinearLayoutManager(Activity) {Orientation = LinearLayoutManager.Vertical};
|
||||
recyclerView.SetLayoutManager(layoutManager);
|
||||
recyclerView.SetAdapter(adapter);
|
||||
recyclerView.ClearOnScrollListeners();
|
||||
recyclerView.AddOnScrollListener(new TripsOnScrollListenerListener(viewModel, layoutManager));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public override async void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
viewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||
if (viewModel.Trips.Count == 0)
|
||||
await viewModel.ExecuteLoadPastTripsCommandAsync();
|
||||
}
|
||||
|
||||
void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(viewModel.IsBusy):
|
||||
refresher.Refreshing = viewModel.IsBusy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStop()
|
||||
{
|
||||
base.OnStop();
|
||||
viewModel.PropertyChanged -= ViewModel_PropertyChanged;
|
||||
}
|
||||
|
||||
void OnItemClick(object sender, TripClickEventArgs args)
|
||||
{
|
||||
var trip = viewModel.Trips[args.Position];
|
||||
var intent = new Intent(Activity, typeof (PastTripDetailsActivity));
|
||||
intent.PutExtra("Id", trip.Id);
|
||||
intent.PutExtra("Rating", trip.Rating);
|
||||
|
||||
PastTripDetailsActivity.Trip = trip;
|
||||
Activity.StartActivity(intent);
|
||||
}
|
||||
|
||||
async void OnItemLongClick(object sender, TripClickEventArgs args)
|
||||
{
|
||||
var trip = viewModel.Trips[args.Position];
|
||||
await viewModel.ExecuteDeleteTripCommand(trip);
|
||||
}
|
||||
|
||||
class TripsOnScrollListenerListener : RecyclerView.OnScrollListener
|
||||
{
|
||||
readonly LinearLayoutManager layoutManager;
|
||||
readonly PastTripsViewModel viewModel;
|
||||
|
||||
public TripsOnScrollListenerListener(PastTripsViewModel viewModel, LinearLayoutManager layoutManager)
|
||||
{
|
||||
this.layoutManager = layoutManager;
|
||||
this.viewModel = viewModel;
|
||||
}
|
||||
|
||||
public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
|
||||
{
|
||||
base.OnScrolled(recyclerView, dx, dy);
|
||||
if (viewModel.IsBusy || viewModel.Trips.Count == 0 || !viewModel.CanLoadMore)
|
||||
return;
|
||||
|
||||
var lastVisiblePosition = layoutManager.FindLastCompletelyVisibleItemPosition();
|
||||
if (lastVisiblePosition == RecyclerView.NoPosition)
|
||||
return;
|
||||
|
||||
//if we are at the bottom and can load more.
|
||||
if (lastVisiblePosition == viewModel.Trips.Count - 1)
|
||||
viewModel.LoadMorePastTripCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TripViewHolder : RecyclerView.ViewHolder
|
||||
{
|
||||
public TripViewHolder(View itemView, Action<TripClickEventArgs> listener,
|
||||
Action<TripClickEventArgs> listenerLong) : base(itemView)
|
||||
{
|
||||
itemView.LongClickable = true;
|
||||
Title = itemView.FindViewById<TextView>(Resource.Id.text_title);
|
||||
Distance = itemView.FindViewById<TextView>(Resource.Id.text_distance);
|
||||
Date = itemView.FindViewById<TextView>(Resource.Id.text_date);
|
||||
Photo = itemView.FindViewById<ImageView>(Resource.Id.photo);
|
||||
itemView.Click +=
|
||||
(sender, e) => listener(new TripClickEventArgs {View = sender as View, Position = AdapterPosition});
|
||||
itemView.LongClick +=
|
||||
(sender, e) => listenerLong(new TripClickEventArgs {View = sender as View, Position = AdapterPosition});
|
||||
}
|
||||
|
||||
public TextView Title { get; set; }
|
||||
public TextView Date { get; set; }
|
||||
public TextView Distance { get; set; }
|
||||
public ImageView Photo { get; set; }
|
||||
}
|
||||
|
||||
public class TripClickEventArgs : EventArgs
|
||||
{
|
||||
public View View { get; set; }
|
||||
public int Position { get; set; }
|
||||
}
|
||||
|
||||
public class TripAdapter : RecyclerView.Adapter
|
||||
{
|
||||
readonly Android.App.Activity activity;
|
||||
readonly PastTripsViewModel viewModel;
|
||||
|
||||
public TripAdapter(Android.App.Activity activity, PastTripsViewModel viewModel)
|
||||
{
|
||||
this.activity = activity;
|
||||
this.viewModel = viewModel;
|
||||
|
||||
this.viewModel.Trips.CollectionChanged +=
|
||||
(sender, e) => { this.activity.RunOnUiThread(NotifyDataSetChanged); };
|
||||
}
|
||||
|
||||
public override int ItemCount => viewModel.Trips.Count;
|
||||
|
||||
public event EventHandler<TripClickEventArgs> ItemClick;
|
||||
public event EventHandler<TripClickEventArgs> ItemLongClick;
|
||||
|
||||
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) =>
|
||||
new TripViewHolder(LayoutInflater.From(parent.Context).Inflate(Resource.Layout.item_trip, parent, false),
|
||||
OnClick, OnClickLong);
|
||||
|
||||
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
|
||||
{
|
||||
var vh = holder as TripViewHolder;
|
||||
if (vh == null)
|
||||
return;
|
||||
|
||||
var trip = viewModel.Trips[position];
|
||||
vh.Title.Text = trip.Name;
|
||||
vh.Distance.Text = trip.TotalDistance;
|
||||
vh.Date.Text = trip.TimeAgo;
|
||||
vh.Photo.Visibility = (trip?.Photos?.Count).GetValueOrDefault() > 0 ||
|
||||
!string.IsNullOrWhiteSpace(trip.MainPhotoUrl)
|
||||
? ViewStates.Visible
|
||||
: ViewStates.Gone;
|
||||
|
||||
if (vh.Photo.Visibility == ViewStates.Visible)
|
||||
{
|
||||
if ((trip?.Photos?.Count).GetValueOrDefault() > 0)
|
||||
Square.Picasso.Picasso.With(activity).Load($"file://{trip.Photos[0].PhotoUrl}").Into(vh.Photo);
|
||||
else
|
||||
Square.Picasso.Picasso.With(activity).Load(trip.MainPhotoUrl).Into(vh.Photo);
|
||||
}
|
||||
}
|
||||
|
||||
void OnClick(TripClickEventArgs args)
|
||||
{
|
||||
ItemClick?.Invoke(this, args);
|
||||
}
|
||||
|
||||
void OnClickLong(TripClickEventArgs args)
|
||||
{
|
||||
ItemLongClick?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
using Android.OS;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Views;
|
||||
using MyDriving.Droid.Controls;
|
||||
using Refractored.Controls;
|
||||
using MyDriving.Utils;
|
||||
using MyDriving.ViewModel;
|
||||
using Android.Widget;
|
||||
|
||||
namespace MyDriving.Droid.Fragments
|
||||
{
|
||||
public class FragmentProfile : Fragment
|
||||
{
|
||||
CircleImageView circleImage;
|
||||
|
||||
TextView distance,
|
||||
maxSpeed,
|
||||
time,
|
||||
stops,
|
||||
accelerations,
|
||||
trips,
|
||||
fuelUsed,
|
||||
distanceUnits,
|
||||
profileRating,
|
||||
profileGreat;
|
||||
|
||||
LinearLayout profileAll;
|
||||
|
||||
RatingCircle ratingCircle;
|
||||
bool refresh = true;
|
||||
ProfileViewModel viewModel;
|
||||
public static FragmentProfile NewInstance() => new FragmentProfile {Arguments = new Bundle()};
|
||||
|
||||
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreateView(inflater, container, savedInstanceState);
|
||||
var view = inflater.Inflate(Resource.Layout.fragment_profile, null);
|
||||
|
||||
ratingCircle = view.FindViewById<RatingCircle>(Resource.Id.rating_circle);
|
||||
circleImage = view.FindViewById<CircleImageView>(Resource.Id.profile_image);
|
||||
|
||||
|
||||
viewModel = new ProfileViewModel();
|
||||
Square.Picasso.Picasso.With(Activity).Load(Settings.Current.UserProfileUrl).Into(circleImage);
|
||||
|
||||
trips = view.FindViewById<TextView>(Resource.Id.text_trips);
|
||||
time = view.FindViewById<TextView>(Resource.Id.text_time);
|
||||
distance = view.FindViewById<TextView>(Resource.Id.text_distance);
|
||||
maxSpeed = view.FindViewById<TextView>(Resource.Id.text_max_speed);
|
||||
fuelUsed = view.FindViewById<TextView>(Resource.Id.text_fuel_consumption);
|
||||
accelerations = view.FindViewById<TextView>(Resource.Id.text_hard_accelerations);
|
||||
stops = view.FindViewById<TextView>(Resource.Id.text_hard_breaks);
|
||||
profileAll = view.FindViewById<LinearLayout>(Resource.Id.text_profile_all);
|
||||
|
||||
profileGreat = view.FindViewById<TextView>(Resource.Id.text_profile_great);
|
||||
profileRating = view.FindViewById<TextView>(Resource.Id.text_profile_rating);
|
||||
profileAll.Visibility = ViewStates.Invisible;
|
||||
UpdateUI();
|
||||
return view;
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
if (refresh)
|
||||
{
|
||||
refresh = false;
|
||||
viewModel.UpdateProfileAsync().ContinueWith(t => UpdateUI());
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateUI()
|
||||
{
|
||||
Activity.RunOnUiThread(() =>
|
||||
{
|
||||
trips.Text = viewModel.TotalTrips.ToString();
|
||||
time.Text = viewModel.TotalTimeDisplay;
|
||||
distance.Text = viewModel.TotalDistanceDisplay;
|
||||
maxSpeed.Text = viewModel.MaxSpeedDisplay;
|
||||
fuelUsed.Text = viewModel.FuelDisplay;
|
||||
accelerations.Text = viewModel.HardAccelerations.ToString();
|
||||
stops.Text = viewModel.HardStops.ToString();
|
||||
ratingCircle.Rating = viewModel.DrivingSkills;
|
||||
|
||||
profileGreat.Text = $"Driving Skills: {viewModel.DrivingSkillsPlacementBucket.Description}";
|
||||
profileRating.Text = $"{viewModel.DrivingSkills.ToString()}%";
|
||||
|
||||
|
||||
profileAll.Visibility = ViewStates.Visible;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for details.
|
||||
|
||||
using Android.OS;
|
||||
using Android.Support.V7.Preferences;
|
||||
using MyDriving.ViewModel;
|
||||
|
||||
namespace MyDriving.Droid.Fragments
|
||||
{
|
||||
public class FragmentSettings : PreferenceFragmentCompat
|
||||
{
|
||||
SettingsViewModel viewModel;
|
||||
public static FragmentSettings NewInstance() => new FragmentSettings {Arguments = new Bundle()};
|
||||
|
||||
public override void OnCreatePreferences(Bundle p0, string p1)
|
||||
{
|
||||
AddPreferencesFromResource(Resource.Xml.preferences);
|
||||
viewModel = new SettingsViewModel();
|
||||
}
|
||||
|
||||
public override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
FindPreference("url_privacy").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.PrivacyPolicyUrl);
|
||||
FindPreference("url_copyright").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.PrivacyPolicyUrl);
|
||||
FindPreference("url_xamarin").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.XamarinUrl);
|
||||
FindPreference("url_terms").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.TermsOfUseUrl);
|
||||
FindPreference("url_open_notice").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.OpenSourceNoticeUrl);
|
||||
FindPreference("url_github").PreferenceClick +=
|
||||
(sender, args) => viewModel.OpenBrowserCommand.Execute(viewModel.SourceOnGitHubUrl);
|
||||
FindPreference("leave_feedback").PreferenceClick +=
|
||||
(sender, args) => HockeyApp.FeedbackManager.ShowFeedbackActivity(Activity);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user