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