New Google Play services stuff like low power location

Discussion in 'Android Questions' started by bluedude, May 28, 2013.

  1. netchicken

    netchicken Active Member Licensed User

    Sorry for the slow response, work is busy :)

    I have been playing with the program this morning but unsure at what I am seeing. The logs show Success Geofence working with GPS turned on. But how do you know when you have crossed a geofence?


    Looks like you have done a ton of work!!!

    Nice going :)
     
    Last edited: Jun 8, 2013
  2. warwound

    warwound Expert Licensed User

    Look in the IntentHandler service, this service is started each time a Geofence entry/exit is detected.
    The service checks if an error has occurred and if not calls the Main Sub UpdateStatus passing the current time and the string "GEOFENCE_TRANSITION_ENTER" or "GEOFENCE_TRANSITION_EXIT".

    So if i start the app and am already within the Geofence i see the time and "GEOFENCE_TRANSITION_ENTER".
    (The service also logs these entry/exit events).

    I'm wondering if the test i did yesterday failed as i had not enabled the LocationRequest - the LocationClient is initialized and connected but no LocationRequest is added, just the Geofence is added.
    The documentation does not state whether Geofence detection will only work if you are also listening for a LocationRequest, and the official demo code does not listen for a LocationRequest while detecting Geofence entry/exit.

    Later today i'll enable both LocationRequest and Geofence in my demo app and take a walk or drive and see if the Geofence detection now works. When i posted previously that Geofence detection was working i had enabled LocationRequest.
    (Your device menu key will show the 'Settings' menu option where these can be enabled/disabled).

    And i'll also be looking at the activity detection features and implementing these in the LocationAPI library.

    Martin.
     
  3. warwound

    warwound Expert Licensed User

    Good news - the activity detection api seems more reliable than the geofence detection!
    It's also much quicker and easier to implement.

    I've attached an example project and an updated version of the LocationAPI library - the LocationAPI library is now version 0.03.

    The example project should detect your movement and update a Label with the name of the detected movement.
    Press the device Home key and the project will continue to detect activity in the background, press the Back key to exit the project.

    Line 75 in the LocationManager service defines the frequency of the checks for activity detection:

    Code:
    ActivityRecognitionClient1.RequestActivityUpdates(1000, PendingIntent1)
    You can see it's set to 1000 milliseconds - you'll probably want to experiment with different values.

    Martin.
     

    Attached Files:

    gudino jose luis likes this.
  4. warwound

    warwound Expert Licensed User

    Here's a sample of what my example activity detection app logged.
    I walked to my car, drove to the supermarket, did a bit of shopping and drove home.
    Looking at my till receipt i was served at 16:00 so the app failed to detect me driving to the supermarket :(.
    It did however detect me driving home and walking from the car to my house :).

    Maybe i shouldn't have set such a long detection period - i set a period of 30 seconds.
    Line 75 of LocationManager:

    Code:
    ActivityRecognitionClient1.RequestActivityUpdates(30000, PendingIntent1)   '   detect activity every 30 seconds
    Overall it seems as though the LocationClient and LocationRequest provide a reliable and maybe energy friendly way to get the device's current position.
    Whether Geolocations can be reliable used or not can only really be decided after more tests.
    ActivityRecognitionClient and DetectedActivity seem to be reliable - though as with all of these new features they are only useful if you set their parameters 'correctly'.

    Martin.
     
    gudino jose luis likes this.
  5. netchicken

    netchicken Active Member Licensed User

    Magnificent!!

    I outputted it to a listview and went for a run which involved driving and then running up a big hill. It worked really well, with only a 10sec - 30 sec lag between activity changeovers.

    Nice work! :)
     
    BillMeyer likes this.
  6. warwound

    warwound Expert Licensed User

    I think what i shall do next then is to rework the library.

    Get rid of the various ????Listener objects and integrate the listeners internally in other other (parent) objects.

    For example the OnAddGeofencesResultListener and OnRemoveGeofencesResultListener objects will go and now the LocationClient will raise the events that those listeners previously raised.
    That'll make things easier - no need to construct the listeners in b4a instead let the library do that.

    With that done i'll return to the Geofence detection and do some more tests - see exactly what is required to make it work reliably.

    I'll work on this over the next few days and then post an update.

    Martin.
     
  7. warwound

    warwound Expert Licensed User

    This looks a bit more promising, another test with the demo posted here: http://www.basic4ppc.com/forum/basi...uff-like-low-power-location-4.html#post174183.

    ExpirationDuration: 7200000 (2 hours)
    FastestInterval: 10000 (10 seconds)
    Interval: 30000 (30 seconds)
    NumUpdates: 9999
    Priority: BALANCED_POWER_ACCURACY

    I created a Geofence with a 50 meter radius around my home and once again drove to the supermarket and back.
    With LocationRequest enabled (it was not enabled in my previous post) everything works just as expected.
    My location regularly logged and exiting then re-entering the Geofence both detected.

    So it looks as though even if you don't want to listen for location updates, you must RequestLocationUpdates in order for Geofence detection to work.

    Martin.
     
  8. bluedude

    bluedude Well-Known Member Licensed User

    Really interesting stuff!

    Hi,

    I think this could be a very valuable library to enable use to write better Location apps.

    I will wait for the re-write of the library and then do extensive testing. Activity recognition is really cool and handy!!!

    Good work!

    Cheers,
     
  9. warwound

    warwound Expert Licensed User

    Yesterday i updated the library and removed the LocationListener, OnAddGeofencesResultListener and OnRemoveGeofencesResultListener objects.
    The Location Client now raises the events that those objects previously raised, meaning you no longer have to create those objects and pass them to the various LocationClient methods.

    This works fine for the OnAddGeofencesResultListener and OnRemoveGeofencesResultListener objects but i can't make up my mind whether or not it's best for the LocationClient to raise the LocationChanged event or to revert to having a LocationListener object.

    Picture this, you create a LocationRequest and call the LocationClient RequestLocationUpdates method.
    You can do this more than once in any activity/service module, so LocationClient could be listening for more than one LocationRequest.

    Now what if you want to stop listening for a particular LocationRequest?

    If LocationClient raises the Location changed event and you call RemoveLocationUpdates then it will stop listening for all LocationRequests.

    If i revert back to having a LocationListener object and you add more than one LocationRequest then each LocationRequest can be added using the same LocationListener OR each LocationRequest can be added using a new LocationListener.
    You can create B4A event handling Subs that handle LocationChanged events from each new LocationListener.

    So you can create LocationRequest1 and LocationListener1 objects and create a Sub that handles LocationListener1 LocationChanged event.
    And you can create LocationRequest2 and LocationListener2 objects and create a Sub that handles LocationListener2 LocationChanged event.
    And importantly you can call RemoveLocationUpdates to stop listening for either LocationRequest1 OR LocationRequest2.

    Have i explained that properly?

    I'm thinking that it will be a little bit more work for the developer to create one or more LocationListener objects BUT that will enable much more control if the developer needs to listen for more than one LocationRequest and stop listening for one LocationRequest while still listening for other LocationRequests.

    I've also found and fixed a bug in the LocationClient GetTriggeringGeofences method which prevented being able to find which Geofence(s) had triggered the Geofence detection to occur.
    And i am working on a much better example project that shows how to use Geofence detection.

    Gonna be busy all day today but will spend more time on the library Tues or Weds and post an update.

    Meanwhile if you can comment on whether or not to have a LocationListener object or not that will be useful.

    Martin.
     
  10. warwound

    warwound Expert Licensed User

    I have another decision to make with the new LocationAPI library regarding Permissions...

    If i hardcode Permissions into the LocationClient it will save the b4a developer having to manually add any Permission to a project using the manifest editor.

    But LocationClient can function with either android.permission.ACCESS_COARSE_LOCATION AND/OR android.permission.ACCESS_FINE_LOCATION.
    Have a read (paragraph 'Specify App Permissions') : Retrieving the Current Location | Android Developers.
    So hardcoding either or both of those two Permissions into the library will initially simplify using the library BUT prevent the developer from explicitly deciding which of those two permissions they want to use.
    I can leave the developer to find the commands and syntax to use to remove an unwanted Permission - it's not difficult.

    I'm tempted to not add any Permission to the LocationClient and make sure i fully document that one of these two Permissions must be manually added to a project using the manifest editor and that the Permission (manually) added will define the accuracy of reported location changes.

    The ActivityRecognitionClient requires com.google.android.gms.permission.ACTIVITY_RECOGNITION, without that Permission it will not work.
    If ActivityRecognitionClient is not used in a project then that Permission will not be added to the project.
    So i think it's ok to leave the ActivityRecognitionClient with com.google.android.gms.permission.ACTIVITY_RECOGNITION hardcoded into it.

    Any comments?

    Martin.
     
  11. bluedude

    bluedude Well-Known Member Licensed User

    It is getting confusing

    In my opinion it is already getting confusing. I haven't been able to get the GEOfencing demo to work out of the box. It logs fine but haven't seen any geofencing stuff on enter etc.

    I also think all settings are already confusing, intervals etc.

    I actually had the impression Google wanted to make it super simple but it does not seem to be very Location Context Aware overall.

    I don't want to fiddle with all kinds of intervals but prefer clear updates and context. Sure, for some things you want 10 seconds updates (like Runkeeper) but for others things you don't.

    For me activity recognition would be the starting point and based on that I would tweak my needs. When I drive I probably need other settings compared to when I walk. Walking is slow so I can never cross 100 km. in an hour so I need less updates/intervals.

    Also I would probably only be interested to see change of activity because that is context aware. When I drive 20 minutes and then walk for 5 I probably parked my car etc. That would need a geofence to find my card back.

    The intervals sound like polling to me.

    Till now none of the demo's really worked for me but I will try again.

    The overall question is if you want to make a pure library that just uses the api's one on one or maybe a smart library that does something more like detecting recognition change etc. and makes it really cool for many apps.

    For now I'm not 100% sure how good the new API's are, need to read a little more.
     
  12. netchicken

    netchicken Active Member Licensed User

    Heh, cycling to work today it once triggered IN_VEHICLE as well as bicycle. I am a cycling god!

    I wonder why course gps detection is necessary, maybe it uses less power?

    Being able to activate and deactivate more than one geofence at a time would be handy.
     
  13. LittleBritaly

    LittleBritaly Member Licensed User

    WARWOUND,

    Have you made any progress with this library ?, I've been away for a couple of weeks but saw your posts via my tablet, I'd like to use your library in my project, but if you're adding new ways to handle stuff would rather wait until you are good to go.
     
  14. warwound

    warwound Expert Licensed User

    I've had a busy week, lots of work to do.
    So my plan is to take some time over the weekend to look again at the library.

    Martin.
     
  15. LittleBritaly

    LittleBritaly Member Licensed User

    Thanks, BTW, you're doing a fantastic job with it.
     
  16. netchicken

    netchicken Active Member Licensed User

    I agree, its wonderful and a great development.
     
  17. walterf25

    walterf25 Well-Known Member Licensed User

    Library

    Where can i get the library for this, i'd like to test this new features also?
     
  18. netchicken

    netchicken Active Member Licensed User

    Walter its on one of Martin's (WW) posts

    Things that I have learned from playing with the Activity program this week. I outputted it to a widget to get real time data on the phone.

    1. It doesn't detect changes very quickly.

    I am running it at 4 seconds and it an be up to 4 calls until it flicks over, and that is fast. Driving it can take ages. However the way I have coded it when it does update to the correct activity the time taken is still correct.

    2. Its not very accurate.

    I suspect Google choose the options it did because the data was inherently inaccurate, so its in 4 wide ranges.

    'Still' is accurate, however the further up the tree the less you can rely on it. 'Vehicle' will show still, walking,and sometimes even bicycle as well as car. However this is understandable when you consider it records the activity at the time, indeed stopped in traffic you are 'still' or moving at 'walking'.

    Walking is very accurate, I have not yet got bicycling from it.

    We could use the confidence rating to detect and help rate accuracy its 1 - 100

    3. I have yet to see any battery drain from it.

    We think of it as calling GPS which is a signif drain on the phone, but its not, its only polling the towers, and maybe changing signal strength.

    Therefore maybe we can use it set at 1 sec if its so small? That might improve accuracy.

    4. Combined with geolocation it could be very good. You are at home, the geoloc triggers only 'still' and then goes to sleep mode. Leave geoloc and it polls faster.

    Its a fun and potentially unique addition to the tool kit, I am making a free app on it presently that should be out in a week or so and incorporating it into my teaching.
     
    Last edited: Jun 15, 2013
  19. netchicken

    netchicken Active Member Licensed User

    Whats the story with the intenthandler?

    I made a new project and under my package name com.ActivityLocation
    my folder is empty, and doesn't hold the Java files.

    Is intenthandler.java generated from the intenthandler service? I imagine that this is the java generated by the program, that we can look in. If so where is the service called?

    Under your path are the 3 java scripts for the program
    Intent1.SetComponent("uk.co.martinpearman.b4a.locationapidemo/.intenthandler")

    So I put them in mine

    Intent1.SetComponent("com.ActivityLocation/.intenthandler")

    I copied them across thinking that the intenthandler.java is being called, but it didn't work.

    Thanks for the help :)
     
  20. warwound

    warwound Expert Licensed User

    Right here's an update on the LocationAPI library - sorry for the delays, been pretty busy with lots of things here.

    This update is i think how i plan to keep the library, previously posted alpha versions of the library and previously posted code examples will need modifying.

    The main change is the removal of the OnAddGeofencesResultListener and OnRemoveGeofencesResultListener.
    Events previously raised by these objects (when adding or removing Geofences) are now raised by the LocationClient.


    Here's the reference for the latest version:

    LocationAPI
    Version: 0.05 alpha
    • ActivityRecognitionClient
      Events:
      • Connected
      • ConnectionFailed (ErrorCode As Int)
      • Disconnected
      Methods:
      • Connect
        Connects the client to Google Play services.
      • Disconnect
        Closes the connection to Google Play services.
      • Initialize (EventName As String)
      • IsConnected As Boolean
        Checks if the client is currently connected to the service, so that requests to other methods will succeed.
      • IsConnecting As Boolean
        Checks if the client is attempting to connect to the service.
      • IsInitialized As Boolean
      • RemoveActivityUpdates (PendingIntent1 As PendingIntent)
        Removes all activity updates for the specified PendingIntent.
      • RequestActivityUpdates (DetectionIntervalMillis As Long, PendingIntent1 As PendingIntent)
        Register for activity recognition updates.
      Permissions:
      • com.google.android.gms.permission.ACTIVITY_RECOGNITION
    • ActivityRecognitionResult
      Methods:
      • GetActivityConfidence (ActivityType As Int) As Int
        Returns the confidence of the given activity type.
      • GetElapsedRealtimeMillis As Long
        Returns the elapsed real time of this detection in milliseconds since boot,
        including time spent in sleep as obtained by SystemClock.elapsedRealtime().
      • GetMostProbableActivity As DetectedActivity
        Returns the most probable activity of the user.
      • GetProbableActivities As List
        Returns a list of activities that where detected with the confidence value associated with each activity.
      • GetTime As Long
        Returns the UTC time of this detection, in milliseconds since January 1, 1970.
      • Initialize (Intent1 As Intent) As Boolean
        Initialize the ActivityRecognitionResult from Intent1.
        If Intent1 contains no ActivityRecognitionResult then the ActivityRecognitionResult will not be initialized and this method will return False.
        Otherwise ActivityRecognitionResult will be initialized and this method will return True.
      • IsInitialized As Boolean
    • DetectedActivity
      Fields:
      • IN_VEHICLE As Int
        The device is in a vehicle, such as a car.
      • ON_BICYCLE As Int
        The device is on a bicycle.
      • ON_FOOT As Int
        The device is on a user who is walking or running.
      • STILL As Int
        The device is still (not moving).
      • TILTING As Int
        The device angle relative to gravity changed significantly.
        This often occurs when a device is picked up from a desk or a user who is sitting stands up.
      • UNKNOWN As Int
        Unable to detect the current activity.
      Methods:
      • GetConfidence As Int
        Returns a value from 0 to 100 indicating the likelihood that the user is performing this activity.
        The larger the value, the more consistent the data used to perform the classification is with the detected activity.
        The sum of the confidences of all detected activities for a classification will be <= 100.
        This means that larger values such as a confidence of >= 75 indicate that it's very likely that the detected activity is correct,
        while a value of <= 50 indicates that there may be another activity that is just as or more likely.
      • GetType As Int
        Returns the type of activity that was detected.
      • Initialize (ActivityType As Int, Confidence As Int)
        Initialize the DetectedActivity.
      • IsInitialized As Boolean
    • Geofence
      Fields:
      • GEOFENCE_TRANSITION_ENTER As Int
        The transition type indicating that the user enters the geofence(s).
      • GEOFENCE_TRANSITION_EXIT As Int
        The transition type indicating that the user exits the geofence(s).
      • NEVER_EXPIRE As Long
        Expiration value that indicates the geofence should never expire.
      Methods:
      • GetRequestId As String
        Returns the request ID of this geofence.
      • IsInitialized As Boolean
    • GeofenceBuilder
      Methods:
      • Build As Geofence
        Creates a Geofence object.
      • Initialize
        Initialize the GeofenceBuilder.
      • IsInitialized As Boolean
      • SetCircularRegion (Latitude As Double, Longitude As Double, Radius As Float) As GeofenceBuilder
        Sets the region of the geofence.
        Latitude - latitude in degrees, between -90 and +90 inclusive.
        Longitude - longitude in degrees, between -180 and +180 inclusive.
        Radius - radius in meters.
      • SetExpirationDuration (DurationMillis As Long) As GeofenceBuilder
        Sets the expiration duration of the geofence.
        The geofence will be removed automatically after this period of time.
        When two geofences with the same requestId are monitored,
        the new one will replace the old one regardless the geographical region these two geofences represent.
      • SetRequestId (RequestId As String) As GeofenceBuilder
        Sets the request ID of the geofence.
        Request ID is a string to identify the geofence inside your application.
      • SetTransitionTypes (TransitionTypes As Int) As GeofenceBuilder
        Sets the transition types of interest.
        Alerts are only generated for the given transition types.
        TransitionTypes - a bitwise-OR of GEOFENCE_TRANSITION_ flags.
    • GooglePlayServicesHelper
      Fields:
      • DEVELOPER_ERROR As Int
      • INTERNAL_ERROR As Int
      • INVALID_ACCOUNT As Int
      • LICENSE_CHECK_FAILED As Int
      • NETWORK_ERROR As Int
      • RESOLUTION_REQUIRED As Int
      • SERVICE_DISABLED As Int
      • SERVICE_INVALID As Int
      • SERVICE_MISSING As Int
      • SERVICE_VERSION_UPDATE_REQUIRED As Int
      • SIGN_IN_REQUIRED As Int
      • SUCCESS As Int
      Methods:
      • IsGooglePlayServicesAvailable As Int
        Verifies that Google Play services is installed and enabled on this device,
        and that the version installed on this device is no older than the one required by this client.
    • LocationClient
      Events:
      • AddGeofencesResult (StatusCode As Int, GeofenceRequestIds() As String)
      • Connected
      • ConnectionFailed (ErrorCode As Int)
      • Disconnected
      • RemoveGeofencesByRequestIdsResult (StatusCode As Int, GeofenceRequestIds() As String)
      Methods:
      • AddGeofences (Geofences As List, PendingIntent1 As PendingIntent)
        Sets alerts to be notified when the device enters or exits one of the specified geofences.
      • Connect
        Connects the client to Google Play services.
      • Disconnect
        Closes the connection to Google Play services.
      • GetErrorCode (Intent1 As Intent) As Int
        Returns the error code that explains the error that triggered Intent1.
      • GetGeofenceTransition (Intent1 As Intent) As Int
        Returns the transition type of geofence transition alert.
      • GetLastLocation As LocationWrapper
        Returns the best most recent location currently available.
        The returned Location object will be uninitialized if no last location is available.
      • GetTriggeringGeofences (Intent1 As Intent) As Geofence[]
        Returns an Array of Geofence objects that triggered this geofence transition alert.
      • HasError (Intent1 As Intent) As Boolean
        Whether an error triggered Intent1.
      • Initialize (EventName As String)
      • IsConnected As Boolean
        Checks if the client is currently connected to the service, so that requests to other methods will succeed.
      • IsConnecting As Boolean
        Checks if the client is attempting to connect to the service.
      • IsInitialized As Boolean
      • RemoveGeofences (GeofenceIds As List)
        Removes geofences by their request IDs.
      • RemoveLocationUpdates (LocationListener1 As LocationListener)
        Removes all location updates.
      • RequestLocationUpdates (LocationRequest1 As LocationRequest, LocationListener1 As LocationListener)
        Requests location updates.
      Permissions:
      • android.permission.ACCESS_FINE_LOCATION
    • LocationListener
      Events:
      • LocationChanged (Location1 As Location)
      Methods:
      • Initialize (EventName As String)
      • IsInitialized As Boolean
    • LocationRequest
      Fields:
      • PRIORITY_BALANCED_POWER_ACCURACY As Int
        Used with SetPriority(Int) to request "block" level accuracy.
      • PRIORITY_HIGH_ACCURACY As Int
        Used with SetPriority(Int) to request the most accurate locations available.
      • PRIORITY_NO_POWER As Int
        Used with SetPriority(Int) to request the best accuracy possible with zero additional power consumption.
      Methods:
      • GetExpirationTime As Long
        Get the request expiration time, in milliseconds since boot.
      • GetFastestInterval As Long
        Get the fastest interval of this request, in milliseconds.
      • GetInterval As Long
        Get the desired interval of this request, in milliseconds.
      • GetNumUpdates As Int
        Get the number of updates requested.
      • GetPriority As Int
        Get the quality of the request.
      • GetSmallestDisplacement As Float
        Get the minimum displacement between location updates in meters.
        By default this is 0.
      • Initialize
        Initialize the LocationRequest with default parameters.
      • IsInitialized As Boolean
      • SetExpirationDuration (Millis As Long) As LocationRequest
        Set the duration of this request, in milliseconds.
      • SetExpirationTime (Millis As Long) As LocationRequest
        Set the request expiration time, in millisecond since boot.
      • SetFastestInterval (Millis As Long) As LocationRequest
        Explicitly set the fastest interval for location updates, in milliseconds.
      • SetInterval (Millis As Long) As LocationRequest
        Set the desired interval for active location updates, in milliseconds.
      • SetNumUpdates (NumUpdates As Int) As LocationRequest
        Set the number of location updates.
      • SetPriority (Priority As Int) As LocationRequest
        Set the priority of the request.
      • SetSmallestDisplacement (SmallestDisplacementMeters As Float) As LocationRequest
        Set the minimum displacement between location updates in meters.
        By default this is 0.
    • LocationStatusCodes
      Fields:
      • ERROR As Int
        An unspecified error occurred; no more specific information is available.
      • GEOFENCE_NOT_AVAILABLE As Int
        Geofence service is not available now.
      • GEOFENCE_TOO_MANY_GEOFENCES As Int
        Your app has registered more than 100 geofences.
      • GEOFENCE_TOO_MANY_PENDING_INTENTS As Int
        You have provided more than 5 different PendingIntents to the addGeofences(List, PendingIntent, OnAddGeofencesResultListener) call.
      • SUCCESS As Int
        The operation was successful.
    • PendingIntent
      Fields:
      • FLAG_CANCEL_CURRENT As Int
        If the described PendingIntent already exists, the current one is cancelled before generating a new one.
      • FLAG_NO_CREATE As Int
        If the described PendingIntent does not already exist, then simply return null instead of creating it.
      • FLAG_ONE_SHOT As Int
        This PendingIntent can only be used once.
      • FLAG_UPDATE_CURRENT As Int
        If the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent.
      Methods:
      • Cancel
        Cancel a currently active PendingIntent.
      • InitializeActivity (RequestCode As Int, Intent1 As Intent, Flags As Int)
      • InitializeBroadcast (RequestCode As Int, Intent1 As Intent, Flags As Int)
      • InitializeService (RequestCode As Int, Intent1 As Intent, Flags As Int)
      • IsInitialized As Boolean

    From the tests i have done and from what netchicken has reported i think the library will provide a reliable way to get the device location using varying degrees of accuracy and varying degrees of battery power.
    Actitivy detection seems to be pretty useable too.

    That leaves the Geofence detection which i have to say i am not at all impressed with!
    My tests have sometimes shown it to work but mostly it has let me down.
    As i've previously posted this is not a problem with the b4a library but a problem with the Location APIs that it wraps - the native java android Location API's Geofence detection has more than a few reports of not working as described in the documentation.

    The latest (alpha) version of the library is attached, i shall post code examples for LocationClient and ActivityRecognitionClient to show basic usage and then do some more tests with the Geofence detection.
    Hopefully i can find a way to improve Geofence detection and i'll then post a code example for that.

    Martin.
     
    Last edited: Feb 12, 2015
    Ohanian likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice