diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 85a0697..7e2ef90 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -87,6 +87,13 @@
+
+
+
+
+
diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/KVV.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/KVV.java
index aae95ac..b96d380 100644
--- a/app/src/main/java/de/sebse/fuplanner/services/kvv/KVV.java
+++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/KVV.java
@@ -56,7 +56,6 @@ public class KVV extends Service {
public KVV() {
Logger log = new Logger(this);
- log.d("constructor");
}
// Binder given to clients
diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java
index 1183158..6525c08 100644
--- a/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java
+++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/ModulesList.java
@@ -39,7 +39,7 @@ public class ModulesList extends HTTPService {
private CacheKVVCourse mKVVCache;
ModulesList(Login login, KVVListener listener, Context context) {
- super(context);
+ super(context, "ModuleList");
this.mLogin = login;
this.mListener = listener;
restore();
diff --git a/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java b/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java
index d52c6e5..9caf96e 100644
--- a/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java
+++ b/app/src/main/java/de/sebse/fuplanner/services/kvv/sync/KVVSyncAdapter.java
@@ -101,9 +101,9 @@ public class KVVSyncAdapter extends AbstractThreadedSyncAdapter {
String authority,
ContentProviderClient provider,
SyncResult syncResult) {
- Intent intent = new Intent(getContext(), KVV.class);
- getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
if (!mBound) {
+ Intent intent = new Intent(getContext(), KVV.class);
+ getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
mWaitForBound = true;
mQueue.add(() -> {});
}
diff --git a/app/src/main/java/de/sebse/fuplanner/tools/NewAsyncQueue.java b/app/src/main/java/de/sebse/fuplanner/tools/NewAsyncQueue.java
index 8a39c91..226b7d1 100644
--- a/app/src/main/java/de/sebse/fuplanner/tools/NewAsyncQueue.java
+++ b/app/src/main/java/de/sebse/fuplanner/tools/NewAsyncQueue.java
@@ -2,14 +2,25 @@ package de.sebse.fuplanner.tools;
import java.util.LinkedList;
+import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class NewAsyncQueue {
private final LinkedList mQueue = new LinkedList<>();
private boolean mIsRunning = false;
+ private Logger log = new Logger(this);
+ public String name = null;
+
+ public NewAsyncQueue(String name) {
+ this.name = name;
+ }
+
+ public NewAsyncQueue() {
+ }
public void add(AsyncQueueCallback callback) {
+ //if ("ModuleList".equals(name)) log.t("add start", name, mIsRunning, mQueue.size());
if (isRunning())
getQueue().addLast(callback);
else {
@@ -19,6 +30,7 @@ public class NewAsyncQueue {
}
public void next() {
+ //if ("ModuleList".equals(name)) log.t("next start", name, mIsRunning, mQueue.size());
AsyncQueueCallback callback = getQueue().pollFirst();
if (callback == null)
setRunning(false);
diff --git a/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPNetwork.java b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPNetwork.java
new file mode 100644
index 0000000..90c7f48
--- /dev/null
+++ b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPNetwork.java
@@ -0,0 +1,225 @@
+package de.sebse.fuplanner.tools.network;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.volley.AuthFailureError;
+import com.android.volley.NetworkResponse;
+import com.android.volley.Request;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.TimeoutError;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.Volley;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import androidx.annotation.Nullable;
+import de.sebse.fuplanner.tools.EventListener;
+import de.sebse.fuplanner.tools.logging.Logger;
+
+/**
+ * Created by sebastian on 24.10.17.
+ */
+
+public class HTTPNetwork extends Service {
+ private RequestQueue requestQueue;
+ protected final Logger log = new Logger(this);
+ private final EventListener errorResponseListener = new EventListener<>();
+ private final EventListener successResponseListener = new EventListener<>();
+ // Binder given to clients
+ private final IBinder mBinder = new LocalBinder();
+
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ requestQueue = Volley.newRequestQueue(getApplicationContext(), new BetterHurlStack(false));
+ }
+
+ public void addErrorListener(String id, EventListener.EventFunction listener) {
+ errorResponseListener.add(id, listener);
+ }
+
+ public void removeErrorListener(String id) {
+ errorResponseListener.remove(id);
+ }
+
+ public void addSuccessListener(String id, EventListener.EventFunction listener) {
+ successResponseListener.add(id, listener);
+ }
+
+ public void removeSuccessListener(String id) {
+ successResponseListener.remove(id);
+ }
+
+ protected void head(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error) {
+ get(url, cookies, response, error, true);
+ }
+
+ protected void get(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error) {
+ get(url, cookies, response, error, false);
+ }
+
+ private void get(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error, boolean noBody) {
+ int requestMethod = noBody ? Request.Method.HEAD : Request.Method.GET;
+ HttpRequest request = new HttpRequest(requestMethod, url, response, error) {
+ @Override
+ public void deliverError(VolleyError error) {
+ if (error == null) {
+ deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
+ } else if (error.networkResponse == null) {
+ int statusCode;
+ if (error instanceof TimeoutError)
+ statusCode = 408;
+ else
+ statusCode = 500;
+ deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
+ } else {
+ final int status = error.networkResponse.statusCode;
+ if (status == 302) {
+ deliverResponse(new Result(null, error.networkResponse.headers));
+ } else {
+ deliver(error);
+ }
+ }
+ }
+
+ @Override
+ protected void deliverResponse(Result response) {
+ successResponseListener.emit(response);
+ super.deliverResponse(response);
+ }
+
+ private void deliver(VolleyError error) {
+ errorResponseListener.emit(error);
+ super.deliverError(error);
+ }
+
+ public Map getHeaders() throws AuthFailureError {
+ Map params = super.getHeaders();
+ if (cookies != null) {
+ if (params==null)
+ params = new HashMap<>();
+ else
+ params = new HashMap<>(params);
+ StringBuilder newStr = new StringBuilder();
+ for (String key : cookies.keySet())
+ newStr.append(key).append("=").append(cookies.get(key)).append(";");
+ newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
+ params.put("Cookie", newStr.toString());
+ }
+
+ return params;
+ }
+ };
+ requestQueue.add(request);
+ }
+
+ protected void post(String url, @Nullable final HashMap cookies, @Nullable final HashMap body, Response.Listener response, Response.ErrorListener error) {
+ HttpRequest request = new HttpRequest(Request.Method.POST, url, response, error) {
+ @Override
+ public String getBodyContentType() {
+ return "application/x-www-form-urlencoded";
+ }
+
+ @Override
+ public byte[] getBody() {
+ if (body==null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for(HashMap.Entry e: body.entrySet()){
+ if(sb.length() > 0){
+ sb.append('&');
+ }
+ try {
+ sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
+ } catch (UnsupportedEncodingException ignored) {
+ }
+ }
+ String requestBody = sb.toString();
+ try {
+ return requestBody.getBytes("utf-8");
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void deliverError(VolleyError error) {
+ if (error == null) {
+ deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
+ } else if (error.networkResponse == null) {
+ int statusCode;
+ if (error instanceof TimeoutError)
+ statusCode = 408;
+ else
+ statusCode = 500;
+ deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
+ } else {
+ final int status = error.networkResponse.statusCode;
+ if (status == 302) {
+ deliverResponse(new Result(null, error.networkResponse.headers));
+ } else {
+ deliver(error);
+ }
+ }
+ }
+
+ private void deliver(VolleyError error) {
+ errorResponseListener.emit(error);
+ super.deliverError(error);
+ }
+
+ @Override
+ protected void deliverResponse(Result response) {
+ successResponseListener.emit(response);
+ super.deliverResponse(response);
+ }
+
+ public Map getHeaders() throws AuthFailureError {
+ Map params = super.getHeaders();
+ if (cookies != null) {
+ if (params==null)
+ params = new HashMap<>();
+ else
+ params = new HashMap<>(params);
+ StringBuilder newStr = new StringBuilder();
+ for (String key : cookies.keySet())
+ newStr.append(key).append("=").append(cookies.get(key)).append(";");
+ newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
+ params.put("Cookie", newStr.toString());
+ }
+
+ return params;
+ }
+ };
+ requestQueue.add(request);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+
+ /**
+ * Class used for the client Binder. Because we know this service always
+ * runs in the same process as its clients, we don't need to deal with IPC.
+ */
+ public class LocalBinder extends Binder {
+ public HTTPNetwork getService() {
+ // Return this instance of LocalService so clients can call public methods
+ return HTTPNetwork.this;
+ }
+ }
+}
diff --git a/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java
index 0425330..a22d6aa 100644
--- a/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java
+++ b/app/src/main/java/de/sebse/fuplanner/tools/network/HTTPService.java
@@ -1,25 +1,22 @@
package de.sebse.fuplanner.tools.network;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.CountDownTimer;
+import android.os.IBinder;
-import com.android.volley.AuthFailureError;
-import com.android.volley.NetworkResponse;
-import com.android.volley.Request;
-import com.android.volley.RequestQueue;
import com.android.volley.Response;
-import com.android.volley.TimeoutError;
import com.android.volley.VolleyError;
-import com.android.volley.toolbox.Volley;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.HashMap;
-import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.Nullable;
import de.sebse.fuplanner.tools.EventListener;
+import de.sebse.fuplanner.tools.NewAsyncQueue;
import de.sebse.fuplanner.tools.logging.Logger;
/**
@@ -27,180 +24,185 @@ import de.sebse.fuplanner.tools.logging.Logger;
*/
public class HTTPService {
- private final RequestQueue requestQueue;
private final Context mContext;
+ private NewAsyncQueue mQueue = new NewAsyncQueue("HTTPService");
protected final Logger log = new Logger(this);
- private final EventListener errorResponseListener = new EventListener<>();
- private final EventListener successResponseListener = new EventListener<>();
+
+ private int mRequestCount = 0;
+ private final HashMap> errorListeners = new HashMap<>();
+ private final HashMap> successListeners = new HashMap<>();
+ private CountDownTimer mDisconnectTimer = null;
+ private HTTPNetwork mService;
+ private boolean mBound = false;
+ private boolean mWaitForBound = false;
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ log.d("connected HTTP", HTTPService.this.mQueue.name, mWaitForBound);
+ HTTPNetwork.LocalBinder binder = (HTTPNetwork.LocalBinder) service;
+ mService = binder.getService();
+ mBound = true;
+ for (String listenerKey: errorListeners.keySet()) {
+ mService.addErrorListener(listenerKey, errorListeners.get(listenerKey));
+ }
+ for (String listenerKey: successListeners.keySet()) {
+ mService.addSuccessListener(listenerKey, successListeners.get(listenerKey));
+ }
+ if (mWaitForBound) {
+ mWaitForBound = false;
+ mQueue.next();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ log.d("disconnected HTTP", HTTPService.this.mQueue.name, mWaitForBound);
+ mBound = false;
+ mService = null;
+ }
+ };
public HTTPService(Context context) {
this.mContext = context;
- requestQueue = Volley.newRequestQueue(context, new BetterHurlStack(false));
+ }
+
+ public HTTPService(Context context, String name) {
+ this.mContext = context;
+ this.mQueue.name = name;
}
public void addErrorListener(String id, EventListener.EventFunction listener) {
- errorResponseListener.add(id, listener);
+ errorListeners.put(id, listener);
+ connect();
+ mQueue.add(() -> {
+ mService.addErrorListener(id, listener);
+ mQueue.next();
+ disconnect();
+ });
}
public void removeErrorListener(String id) {
- errorResponseListener.remove(id);
+ errorListeners.remove(id);
+ connect();
+ mQueue.add(() -> {
+ mService.removeErrorListener(id);
+ mQueue.next();
+ disconnect();
+ });
}
public void addSuccessListener(String id, EventListener.EventFunction listener) {
- successResponseListener.add(id, listener);
+ successListeners.put(id, listener);
+ connect();
+ mQueue.add(() -> {
+ mService.addSuccessListener(id, listener);
+ mQueue.next();
+ disconnect();
+ });
}
public void removeSuccessListener(String id) {
- successResponseListener.remove(id);
+ successListeners.remove(id);
+ connect();
+ mQueue.add(() -> {
+ mService.removeSuccessListener(id);
+ mQueue.next();
+ disconnect();
+ });
}
protected void head(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error) {
- get(url, cookies, response, error, true);
+ log.d("HEAD", url);
+ connect();
+ mQueue.add(() -> {
+ mService.head(url, cookies, response1 -> {
+ log.d("response", url);
+ response.onResponse(response1);
+ disconnect();
+ }, error1 -> {
+ log.d("error", url);
+ error.onErrorResponse(error1);
+ disconnect();
+ });
+ mQueue.next();
+ });
}
protected void get(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error) {
- get(url, cookies, response, error, false);
- }
-
- private void get(String url, @Nullable final HashMap cookies, Response.Listener response, Response.ErrorListener error, boolean noBody) {
- int requestMethod = noBody ? Request.Method.HEAD : Request.Method.GET;
- HttpRequest request = new HttpRequest(requestMethod, url, response, error) {
- @Override
- public void deliverError(VolleyError error) {
- if (error == null) {
- deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
- } else if (error.networkResponse == null) {
- int statusCode;
- if (error instanceof TimeoutError)
- statusCode = 408;
- else
- statusCode = 500;
- deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
- } else {
- final int status = error.networkResponse.statusCode;
- if (status == 302) {
- deliverResponse(new Result(null, error.networkResponse.headers));
- } else {
- deliver(error);
- }
- }
- }
-
- @Override
- protected void deliverResponse(Result response) {
- successResponseListener.emit(response);
- super.deliverResponse(response);
- }
-
- private void deliver(VolleyError error) {
- errorResponseListener.emit(error);
- super.deliverError(error);
- }
-
- public Map getHeaders() throws AuthFailureError {
- Map params = super.getHeaders();
- if (cookies != null) {
- if (params==null)
- params = new HashMap<>();
- else
- params = new HashMap<>(params);
- StringBuilder newStr = new StringBuilder();
- for (String key : cookies.keySet())
- newStr.append(key).append("=").append(cookies.get(key)).append(";");
- newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
- params.put("Cookie", newStr.toString());
- }
-
- return params;
- }
- };
- requestQueue.add(request);
+ log.d("GET", url);
+ connect();
+ mQueue.add(() -> {
+ mService.get(url, cookies, response1 -> {
+ log.d("response", url);
+ response.onResponse(response1);
+ disconnect();
+ }, error1 -> {
+ log.d("error", url);
+ error.onErrorResponse(error1);
+ disconnect();
+ });
+ mQueue.next();
+ });
}
protected void post(String url, @Nullable final HashMap cookies, @Nullable final HashMap body, Response.Listener response, Response.ErrorListener error) {
- HttpRequest request = new HttpRequest(Request.Method.POST, url, response, error) {
- @Override
- public String getBodyContentType() {
- return "application/x-www-form-urlencoded";
- }
-
- @Override
- public byte[] getBody() {
- if (body==null) {
- return null;
- }
- StringBuilder sb = new StringBuilder();
- for(HashMap.Entry e: body.entrySet()){
- if(sb.length() > 0){
- sb.append('&');
- }
- try {
- sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
- } catch (UnsupportedEncodingException ignored) {
- }
- }
- String requestBody = sb.toString();
- try {
- return requestBody.getBytes("utf-8");
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- }
-
- @Override
- public void deliverError(VolleyError error) {
- if (error == null) {
- deliver(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
- } else if (error.networkResponse == null) {
- int statusCode;
- if (error instanceof TimeoutError)
- statusCode = 408;
- else
- statusCode = 500;
- deliver(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
- } else {
- final int status = error.networkResponse.statusCode;
- if (status == 302) {
- deliverResponse(new Result(null, error.networkResponse.headers));
- } else {
- deliver(error);
- }
- }
- }
-
- private void deliver(VolleyError error) {
- errorResponseListener.emit(error);
- super.deliverError(error);
- }
-
- @Override
- protected void deliverResponse(Result response) {
- successResponseListener.emit(response);
- super.deliverResponse(response);
- }
-
- public Map getHeaders() throws AuthFailureError {
- Map params = super.getHeaders();
- if (cookies != null) {
- if (params==null)
- params = new HashMap<>();
- else
- params = new HashMap<>(params);
- StringBuilder newStr = new StringBuilder();
- for (String key : cookies.keySet())
- newStr.append(key).append("=").append(cookies.get(key)).append(";");
- newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
- params.put("Cookie", newStr.toString());
- }
-
- return params;
- }
- };
- requestQueue.add(request);
+ log.d("POST", url);
+ connect();
+ mQueue.add(() -> {
+ mService.post(url, cookies, body, response1 -> {
+ log.d("response", url);
+ response.onResponse(response1);
+ disconnect();
+ }, error1 -> {
+ log.d("error", url);
+ error.onErrorResponse(error1);
+ disconnect();
+ });
+ mQueue.next();
+ });
}
- protected Context getContext() {
- return mContext;
+ private void connect() {
+ if (!mBound) {
+ Intent intent = new Intent(getContext(), HTTPNetwork.class);
+ getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ if (!mWaitForBound) mQueue.add(() -> {});
+ mWaitForBound = true;
+ }
+ if (mDisconnectTimer != null) {
+ mDisconnectTimer.cancel();
+ mDisconnectTimer = null;
+ }
+ mRequestCount++;
+ log.d("connect count", HTTPService.this.mQueue.name, mRequestCount);
+ }
+
+ private void disconnect() {
+ mRequestCount--;
+ log.d("disconnect count", HTTPService.this.mQueue.name, mRequestCount);
+ if (mDisconnectTimer == null) {
+ if (mBound && mRequestCount == 0) {
+ mBound = false;
+ mService = null;
+ getContext().unbindService(mConnection);
+ }
+ /*mDisconnectTimer = new CountDownTimer(60 * 1000, 60 * 1000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+
+ }
+
+ @Override
+ public void onFinish() {
+ if (mBound) {
+ getContext().unbindService(mConnection);
+ mQueue.add(() -> {});
+ mWaitForUnbound = true;
+ }
+ }
+ };*/
+ }
}
@@ -222,4 +224,8 @@ public class HTTPService {
}
return result;
}
+
+ protected Context getContext() {
+ return mContext;
+ }
}
diff --git a/app/src/main/res/xml/service_kvv.xml b/app/src/main/res/xml/service_kvv.xml
deleted file mode 100644
index 1dbe851..0000000
--- a/app/src/main/res/xml/service_kvv.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-