diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
index c95b841..93404d3 100644
--- a/.idea/assetWizardSettings.xml
+++ b/.idea/assetWizardSettings.xml
@@ -18,7 +18,7 @@
@@ -28,7 +28,7 @@
diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
deleted file mode 100644
index 0f29f67..0000000
Binary files a/.idea/caches/build_file_checksums.ser and /dev/null differ
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 30aa626..681f41a 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,29 +1,116 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..15a15b2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 99202cc..b9be38f 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -5,22 +5,36 @@
diff --git a/app/build.gradle b/app/build.gradle
index 9d97c1b..6a5015b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,14 +1,18 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-android'
android {
- compileSdkVersion 28
+ compileSdkVersion 29
defaultConfig {
applicationId "com.gueg.velovwidget"
minSdkVersion 21
- targetSdkVersion 28
+ targetSdkVersion 29
versionCode 3
versionName "1.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ renderscriptTargetApi 22
+ renderscriptSupportModeEnabled true
}
buildTypes {
release {
@@ -22,13 +26,19 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
- implementation 'com.google.code.gson:gson:2.8.0'
+ implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.osmdroid:osmdroid-android:6.0.2'
+ implementation 'fr.tvbarthel.blurdialogfragment:lib:2.2.0'
+ implementation 'com.android.volley:volley:1.1.1'
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:$room_version"
implementation "android.arch.persistence.room:rxjava2:$room_version"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
+repositories {
+ mavenCentral()
+}
diff --git a/app/src/main/java/com/gueg/velovwidget/MainListActivity.java b/app/src/main/java/com/gueg/velovwidget/MainListActivity.java
index 074205b..68fa15d 100644
--- a/app/src/main/java/com/gueg/velovwidget/MainListActivity.java
+++ b/app/src/main/java/com/gueg/velovwidget/MainListActivity.java
@@ -11,7 +11,9 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.widget.ImageView;
import android.widget.ProgressBar;
+import android.widget.TextView;
import com.gueg.velovwidget.database_stations.JsonParser;
import com.gueg.velovwidget.map.PinsActivity;
@@ -33,6 +35,9 @@ public class MainListActivity extends AppCompatActivity {
SwipeRefreshLayout _swipe;
ProgressBar _progress;
+ ImageView _serverIcon;
+ TextView _serverText;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -41,6 +46,9 @@ public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_list);
+ _serverIcon = findViewById(R.id.widget_list_servers_icon);
+ _serverText = findViewById(R.id.widget_list_servers_text);
+
_swipe = findViewById(R.id.widget_list_refresh);
_list = findViewById(R.id.widget_list_stations);
_progress = findViewById(R.id.widget_list_progress);
@@ -49,6 +57,8 @@ public void onCreate(Bundle savedInstanceState) {
_adapter.setListener(new MainListAdapter.RefreshListener() {
@Override public void onRefreshStarted() {
_swipe.setRefreshing(true);
+ _serverIcon.setImageDrawable(getApplicationContext().getResources().getDrawable(R.drawable.ic_server_loading));
+ _serverText.setText(getApplicationContext().getResources().getString(R.string.servers_loading));
}
@Override public void onProgressChanged(int progress, int max) {
if(_progress.getVisibility()!= View.VISIBLE)
@@ -62,6 +72,15 @@ public void onCreate(Bundle savedInstanceState) {
if(_progress.getProgress()==0)
startActivityForResult(new Intent(MainListActivity.this, PinsActivity.class), ACTIVITY_PINS);
}
+ @Override public void onServerResult(boolean err) {
+ if(err) {
+ _serverIcon.setImageDrawable(getApplicationContext().getResources().getDrawable(R.drawable.ic_server_offline));
+ _serverText.setText(getApplicationContext().getResources().getString(R.string.servers_offline));
+ } else {
+ _serverIcon.setImageDrawable(getApplicationContext().getResources().getDrawable(R.drawable.ic_server_online));
+ _serverText.setText(getApplicationContext().getResources().getString(R.string.servers_online));
+ }
+ }
});
_list.setAdapter(_adapter);
_list.setLayoutManager(new LinearLayoutManager(this));
@@ -95,8 +114,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
return;
switch(requestCode) {
case ACTIVITY_PINS:
- _adapter.refresh();
- break;
case ACTIVITY_SORT:
_adapter.refresh();
break;
diff --git a/app/src/main/java/com/gueg/velovwidget/MainListAdapter.java b/app/src/main/java/com/gueg/velovwidget/MainListAdapter.java
index 3ab4d2a..5c183e7 100644
--- a/app/src/main/java/com/gueg/velovwidget/MainListAdapter.java
+++ b/app/src/main/java/com/gueg/velovwidget/MainListAdapter.java
@@ -5,6 +5,8 @@
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,6 +18,14 @@
import com.gueg.velovwidget.database_stations.JsonParser;
import com.gueg.velovwidget.database_stations.WidgetItemsDatabase;
+import com.gueg.velovwidget.velov.TokenManager;
+import com.gueg.velovwidget.velov.Velov;
+import com.gueg.velovwidget.velov.VelovDialog;
+import com.gueg.velovwidget.velov.VelovParser;
+import com.gueg.velovwidget.velov.VelovRequest;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
@@ -29,7 +39,7 @@ public class MainListAdapter extends RecyclerView.Adapter _list;
private Context c;
-
+ private FragmentManager fm;
static class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout bkg, dynamicDataLayout;
@@ -48,8 +58,9 @@ static class ViewHolder extends RecyclerView.ViewHolder {
}
}
- public MainListAdapter(Context c) {
- this.c = c;
+ public MainListAdapter(AppCompatActivity a) {
+ this.c = a;
+ this.fm = a.getSupportFragmentManager();
_list = new ArrayList<>();
}
@@ -74,11 +85,22 @@ public void onBindViewHolder(@NonNull final MainListAdapter.ViewHolder holder, f
holder.dynamicDataLayout.setVisibility(View.GONE);
holder.title.setTextColor(c.getResources().getColor(R.color.colorTextWhite));
holder.bkg.setBackgroundColor(c.getResources().getColor(R.color.colorPrimary));
+
+ holder.bkg.setOnClickListener(null);
} else {
holder.dynamicDataLayout.setVisibility(View.VISIBLE);
holder.title.setTextColor(c.getResources().getColor(R.color.colorTextBlack));
holder.bkg.setBackgroundColor(c.getResources().getColor(R.color.colorTextWhite));
+ holder.bkg.setTag(R.id.tag_name, item.name);
+ holder.bkg.setTag(R.id.tag_number, item.number);
+
+ holder.bkg.setOnClickListener(new View.OnClickListener() {
+ @Override public void onClick(View view) {
+ new VelovDialog().setStationName((String)view.getTag(R.id.tag_name)).setStationNb((int)view.getTag(R.id.tag_number)).show(fm, null);
+ }
+ });
+
if(item.data == null) {
holder.loading.setVisibility(View.VISIBLE);
holder.title.setTextColor(c.getResources().getColor(R.color.colorTextBlack));
@@ -145,10 +167,35 @@ public void run() {
MainListAdapter.this.notifyDataSetChanged();
}
});
+ boolean hasServersBeenTested = false;
for(int i=0; i<_list.size(); i++) {
final int pos = i;
- if(!_list.get(i).isSeparator())
+ if(!_list.get(i).isSeparator()) {
_list.get(i).setData(JsonParser.updateDynamicDataFromApi(_list.get(i)).data);
+ if(!hasServersBeenTested) {
+ final int stationId = _list.get(i).number;
+
+ TokenManager.Companion.getToken(c, new TokenManager.TokenManagerListener() {
+ @Override
+ public void onTokenParsed(@Nullable String token) {
+ if(token == null) {
+ _listener.onServerResult(true); // error
+ return;
+ }
+
+ VelovParser.Companion.parse(c, new VelovRequest(stationId, token), new VelovParser.VelovParserListener() {
+ @Override public void onParseComplete(@NotNull ArrayList velovs) {
+ _listener.onServerResult(false); // no error
+ }
+ @Override public void onParseError() {
+ _listener.onServerResult(true); // error
+ }
+ });
+ }
+ });
+ hasServersBeenTested = true;
+ }
+ }
((MainListActivity)c).runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -184,5 +231,6 @@ public interface RefreshListener {
void onRefreshStarted();
void onProgressChanged(int progress, int max);
void onRefreshFinished();
+ void onServerResult(boolean err);
}
}
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/TokenManager.kt b/app/src/main/java/com/gueg/velovwidget/velov/TokenManager.kt
new file mode 100644
index 0000000..76feff7
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/TokenManager.kt
@@ -0,0 +1,46 @@
+package com.gueg.velovwidget.velov
+
+import android.content.Context
+import com.android.volley.Response
+import com.android.volley.toolbox.Volley
+import org.json.JSONObject
+
+class TokenManager {
+ companion object {
+ private var token = ""
+
+ fun getToken(context: Context, listener: TokenManagerListener) {
+ if(token.isNotEmpty())
+ listener.onTokenParsed(token)
+ else {
+ val queue = Volley.newRequestQueue(context)
+ queue.add(
+ TokenRequestOptions.build(
+ Response.Listener {
+ queue.add(TokenRequest.build(
+ Response.Listener { tokenResponse ->
+ val tokenJsonResponse = JSONObject(tokenResponse)
+
+ token = tokenJsonResponse["accessToken"] as String
+
+ listener.onTokenParsed(token)
+ },
+ Response.ErrorListener { err ->
+ err.printStackTrace()
+ listener.onTokenParsed(null)
+ }))
+ },
+ Response.ErrorListener { err ->
+ err.printStackTrace()
+ listener.onTokenParsed(null)
+ }
+ )
+ )
+ }
+ }
+ }
+
+ interface TokenManagerListener {
+ fun onTokenParsed(token: String?)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/TokenRequest.kt b/app/src/main/java/com/gueg/velovwidget/velov/TokenRequest.kt
new file mode 100644
index 0000000..d76b81b
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/TokenRequest.kt
@@ -0,0 +1,56 @@
+package com.gueg.velovwidget.velov
+
+import com.android.volley.Response
+import com.android.volley.toolbox.StringRequest
+import java.util.*
+
+class TokenRequest {
+
+ companion object {
+ @Suppress("PrivatePropertyName")
+ private val LINK = "https://api.cyclocity.fr/auth/environments/PRD/client_tokens"
+
+ fun build(responseListener: Response.Listener, errorListener: Response.ErrorListener): StringRequest {
+ return object : StringRequest(
+ Method.POST,
+ LINK,
+ responseListener,
+ errorListener
+ ) {
+ override fun getHeaders(): Map {
+ val headers = HashMap()
+ headers["Accept"] = "application/json, text/plain, */*"
+ headers["Accept-Language"] = "fr-FR,fr;en-US,en;q=0.8;q=0.6;q=0.4"
+ headers["Access-Control-Allow-Headers"] = "*"
+ headers["Access-Control-Allow-Origin"] = "*"
+ headers["Connection"] = "keep-alive"
+ headers["Content-Length"] = "100"
+ headers["Content-Type"] = "application/json"
+ headers["DNT"] = "1"
+ headers["Host"] = "api.cyclocity.fr"
+ headers["Origin"] = "https://velov.grandlyon.com"
+ headers["Referer"] = "https://velov.grandlyon.com/mapping"
+ headers["Sec-Fetch-Mode"] = "cors"
+ headers["Sec-Fetch-Site"] = "cross-site"
+ headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 OPR/64.0.3417.92"
+
+ headers["refreshToken"] = "c1108f14-714c-4ef9-8fc1-5cd8bc127ae6"
+
+ return headers
+ }
+
+ override fun getBody(): ByteArray {
+ val payload =
+ "{\"code\":\"vls.web.lyon:PRD\","+
+ "\"key\":\"c3d9f5c22a9157a7cc7fe0e38269573bdd2f13ec48f867360ecdcbd35b196f87\"}"
+
+ return payload.toByteArray()
+ }
+
+ override fun getBodyContentType(): String {
+ return "application/json"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/TokenRequestOptions.kt b/app/src/main/java/com/gueg/velovwidget/velov/TokenRequestOptions.kt
new file mode 100644
index 0000000..1472778
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/TokenRequestOptions.kt
@@ -0,0 +1,40 @@
+package com.gueg.velovwidget.velov
+
+import com.android.volley.Response
+import com.android.volley.toolbox.StringRequest
+import java.util.*
+
+class TokenRequestOptions {
+
+ companion object {
+ @Suppress("PrivatePropertyName")
+ private val LINK = "https://api.cyclocity.fr/auth/environments/PRD/client_tokens"
+
+ fun build(responseListener: Response.Listener, errorListener: Response.ErrorListener): StringRequest {
+ return object : StringRequest(
+ Method.OPTIONS,
+ LINK,
+ responseListener,
+ errorListener
+ ) {
+ override fun getHeaders(): Map {
+ val headers = HashMap()
+ headers["Sec-Fetch-Mode"] = "cors"
+ headers["Sec-Fetch-Site"] = "cross-site"
+ headers["DNT"] = "1"
+ headers["Connection"] = "keep-alive"
+ headers["Content-Type"] = "application/json"
+ headers["Accept"] = "*/*"
+ headers["Host"] = "api.cyclocity.fr"
+ headers["Origin"] = "https://velov.grandlyon.com"
+ headers["Referer"] = "https://velov.grandlyon.com/mapping"
+ headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 OPR/64.0.3417.92"
+ headers["Access-Control-Request-Headers"] = "access-control-allow-headers,access-control-allow-origin,content-type"
+ headers["Access-Control-Request-Method"] = "POST"
+
+ return headers
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/Velov.kt b/app/src/main/java/com/gueg/velovwidget/velov/Velov.kt
new file mode 100644
index 0000000..9af8bd1
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/Velov.kt
@@ -0,0 +1,13 @@
+package com.gueg.velovwidget.velov
+
+import java.util.*
+
+data class Velov(
+ val standNumber: Int,
+ val status: String,
+ val rating: Double,
+ val ratingCount: Int,
+ val ratingLastDate: Date,
+ val createdAt: Date,
+ val updatedAt: Date
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/VelovAdapter.kt b/app/src/main/java/com/gueg/velovwidget/velov/VelovAdapter.kt
new file mode 100644
index 0000000..bf84dc3
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/VelovAdapter.kt
@@ -0,0 +1,28 @@
+package com.gueg.velovwidget.velov
+
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.gueg.velovwidget.R
+
+class VelovAdapter(private val velovs: List) : RecyclerView.Adapter() {
+
+ inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ var rating: TextView = v.findViewById(R.id.velov_rating)
+ var standNb: TextView = v.findViewById(R.id.velov_stand_nb)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, pos: Int) {
+ val velov = velovs[pos]
+
+ holder.rating.text = velov.rating.toString()
+ holder.standNb.text = velov.standNumber.toString()
+ }
+
+ override fun onCreateViewHolder(p0: ViewGroup, p1: Int) =ViewHolder(LayoutInflater.from(p0.context).inflate(R.layout.row_velov, p0, false))
+
+ override fun getItemCount(): Int = velovs.size
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/VelovDialog.kt b/app/src/main/java/com/gueg/velovwidget/velov/VelovDialog.kt
new file mode 100644
index 0000000..0b206e1
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/VelovDialog.kt
@@ -0,0 +1,88 @@
+package com.gueg.velovwidget.velov
+
+import android.os.Bundle
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
+import android.view.ViewGroup
+import android.widget.ProgressBar
+import android.widget.TextView
+import com.gueg.velovwidget.R
+import fr.tvbarthel.lib.blurdialogfragment.SupportBlurDialogFragment
+
+class VelovDialog : SupportBlurDialogFragment() {
+
+ private lateinit var rootView: View
+ private lateinit var title: TextView
+
+ private lateinit var list: RecyclerView
+ private lateinit var noVelov: TextView
+ private lateinit var progress: ProgressBar
+
+ private lateinit var stationName: String
+ private var stationNumber = 0
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ rootView = inflater.inflate(R.layout.dialog_velov, container, false)
+
+ title = rootView.findViewById(R.id.dialog_velov_stationname)
+ title.text = stationName
+
+ noVelov = rootView.findViewById(R.id.dialog_velov_novelov)
+ progress = rootView.findViewById(R.id.dialog_velov_loading)
+
+ list = rootView.findViewById(R.id.dialog_velov_stationlist)
+ list.layoutManager = LinearLayoutManager(context)
+
+ TokenManager.getToken(context!!, object: TokenManager.TokenManagerListener {
+ override fun onTokenParsed(token: String?) {
+ if(token == null) {
+ onError()
+ return
+ }
+
+ VelovParser.parse(context!!, VelovRequest(stationNumber, token), object: VelovParser.VelovParserListener {
+ override fun onParseComplete(velovs: ArrayList) {
+ activity!!.runOnUiThread {
+ progress.visibility = GONE
+ if(velovs.size>0) {
+ list.visibility = VISIBLE
+
+ list.adapter = VelovAdapter(velovs.sortedWith(compareBy{ it.standNumber }))
+ } else {
+ noVelov.visibility = VISIBLE
+ }
+ }
+ }
+ override fun onParseError() {
+ onError()
+ }
+ })
+ }
+ })
+
+
+ return rootView
+ }
+
+ private fun onError() {
+ activity!!.runOnUiThread {
+ progress.visibility = GONE
+ noVelov.visibility = VISIBLE
+ noVelov.text = "Erreur de récupération des vélovs."
+ }
+ }
+
+ fun setStationName(stationName: String) : VelovDialog {
+ this.stationName = stationName
+ return this
+ }
+
+ fun setStationNb(stationNb: Int) : VelovDialog {
+ this.stationNumber = stationNb
+ return this
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/VelovParser.kt b/app/src/main/java/com/gueg/velovwidget/velov/VelovParser.kt
new file mode 100644
index 0000000..261b4b1
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/VelovParser.kt
@@ -0,0 +1,63 @@
+package com.gueg.velovwidget.velov
+
+import android.content.Context
+import com.android.volley.Response
+import com.android.volley.toolbox.Volley
+import org.json.JSONArray
+import org.json.JSONObject
+import java.text.SimpleDateFormat
+import java.util.*
+import kotlin.collections.ArrayList
+
+class VelovParser {
+ companion object {
+ fun parse(context: Context, request: VelovRequest, listener: VelovParserListener) {
+ Thread {
+ val queue = Volley.newRequestQueue(context)
+
+ queue.add(request.build(
+ Response.Listener { response ->
+ val jsonResponse = JSONArray(response)
+ val velovs = ArrayList()
+
+ if(jsonResponse.length()>0) {
+ for(i in 0 until jsonResponse.length()) {
+ val json = jsonResponse[i] as JSONObject
+ val rating = json["rating"] as JSONObject
+
+ val velov = Velov(
+ json["standNumber"] as Int,
+ json["status"] as String,
+ rating["value"] as Double,
+ rating["count"] as Int,
+ formatDate(rating["lastRatingDateTime"] as String),
+ formatDate(json["createdAt"] as String),
+ formatDate(json["updatedAt"] as String)
+ )
+
+ velovs.add(velov)
+ }
+ }
+
+ listener.onParseComplete(velovs)
+ },
+ Response.ErrorListener { err ->
+ err.printStackTrace()
+ listener.onParseError()
+ }
+ ))
+
+ }.start()
+ }
+
+ // 2019-11-04T19:42:21.325
+ private fun formatDate(str: String): Date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS").parse(str)!!
+
+ }
+
+
+ interface VelovParserListener {
+ fun onParseComplete(velovs: ArrayList)
+ fun onParseError()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gueg/velovwidget/velov/VelovRequest.kt b/app/src/main/java/com/gueg/velovwidget/velov/VelovRequest.kt
new file mode 100644
index 0000000..0feb83f
--- /dev/null
+++ b/app/src/main/java/com/gueg/velovwidget/velov/VelovRequest.kt
@@ -0,0 +1,34 @@
+package com.gueg.velovwidget.velov
+
+import com.android.volley.Response
+import com.android.volley.toolbox.StringRequest
+import java.util.*
+
+class VelovRequest(private var stationNb: Int, private val token: String) {
+
+ @Suppress("PrivatePropertyName")
+ private val LINK = "https://api.cyclocity.fr/contracts/lyon/bikes?stationNumber=#STANB#"
+
+ fun build(responseListener : Response.Listener, errorListener : Response.ErrorListener) : StringRequest {
+ return object : StringRequest(
+ Method.GET,
+ LINK.replace("#STANB#", stationNb.toString()),
+ responseListener,
+ errorListener
+ ) {
+ override fun getHeaders(): Map {
+ val headers = HashMap()
+ headers["Sec-Fetch-Mode"] = "cors"
+ headers["DNT"] = "1"
+ headers["Accept-Language"] = "fr"
+ headers["Authorization"] = "Taknv1 $token"
+ headers["Content-Type"] = "application/vnd.bikes.v2+json"
+ headers["Accept"] = "application/vnd.bikes.v2+json"
+ headers["Referer"] = "https://velov.grandlyon.com/mapping"
+ headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36 OPR/64.0.3417.92"
+
+ return headers
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_server_loading.xml b/app/src/main/res/drawable/ic_server_loading.xml
new file mode 100644
index 0000000..a69d050
--- /dev/null
+++ b/app/src/main/res/drawable/ic_server_loading.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_server_offline.xml b/app/src/main/res/drawable/ic_server_offline.xml
new file mode 100644
index 0000000..1773785
--- /dev/null
+++ b/app/src/main/res/drawable/ic_server_offline.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_server_online.xml b/app/src/main/res/drawable/ic_server_online.xml
new file mode 100644
index 0000000..a5edc64
--- /dev/null
+++ b/app/src/main/res/drawable/ic_server_online.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_star.xml b/app/src/main/res/drawable/ic_star.xml
new file mode 100644
index 0000000..a87ca09
--- /dev/null
+++ b/app/src/main/res/drawable/ic_star.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/activity_list.xml
index ec02597..409afba 100644
--- a/app/src/main/res/layout/activity_list.xml
+++ b/app/src/main/res/layout/activity_list.xml
@@ -3,10 +3,43 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:fab="http://schemas.android.com/apk/res-auto">
+
+
+
+
+
+
+
+ android:layout_height="match_parent"
+
+ android:layout_below="@id/widget_list_servers_layout">
+
+ android:progressTint="@color/colorPrimary"
+ android:scaleY="2" />
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/row_velov.xml b/app/src/main/res/layout/row_velov.xml
new file mode 100644
index 0000000..35f6466
--- /dev/null
+++ b/app/src/main/res/layout/row_velov.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 59960f9..4964c5e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,7 +1,14 @@
+
+
+
Vélo\'v watcher
Dernière actualisation :
+ Serveurs en ligne
+ Test des serveurs...
+ Serveurs hors ligne
+
Changer de ville
Rafraichir
Configurer
diff --git a/build.gradle b/build.gradle
index f3fd618..9022dfa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,15 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
-
+ ext.kotlin_version = '1.3.60'
+
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.3'
-
+ classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
// 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 669933b..a6448a9 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Jul 26 16:47:40 CEST 2018
+#Wed Nov 20 07:57:51 CET 2019
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-5.4.1-all.zip