Java tutorial
/* * Copyright 2017 Thomas Andres * * This file is part of Kandroid. * * Kandroid is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Kandroid is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see <http://www.gnu.org/licenses/>. */ package in.andres.kandroid.kanboard; import android.graphics.Color; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.Authenticator; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.PasswordAuthentication; import java.net.ProtocolException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Date; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.concurrent.ThreadPoolExecutor; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import in.andres.kandroid.BuildConfig; import in.andres.kandroid.Constants; import in.andres.kandroid.kanboard.events.OnCloseTaskListener; import in.andres.kandroid.kanboard.events.OnCreateCommentListener; import in.andres.kandroid.kanboard.events.OnCreateSubtaskListener; import in.andres.kandroid.kanboard.events.OnCreateTaskListener; import in.andres.kandroid.kanboard.events.OnErrorListener; import in.andres.kandroid.kanboard.events.OnGetActiveSwimlanesListener; import in.andres.kandroid.kanboard.events.OnGetAllCategoriesListener; import in.andres.kandroid.kanboard.events.OnGetAllCommentsListener; import in.andres.kandroid.kanboard.events.OnGetAllSubtasksListener; import in.andres.kandroid.kanboard.events.OnGetAllTasksListener; import in.andres.kandroid.kanboard.events.OnGetCategoryListener; import in.andres.kandroid.kanboard.events.OnGetColumnsListener; import in.andres.kandroid.kanboard.events.OnGetDefaultColorListener; import in.andres.kandroid.kanboard.events.OnGetDefaultColorsListener; import in.andres.kandroid.kanboard.events.OnGetMeListener; import in.andres.kandroid.kanboard.events.OnGetMyActivityStreamListener; import in.andres.kandroid.kanboard.events.OnGetMyDashboardListener; import in.andres.kandroid.kanboard.events.OnGetMyOverdueTasksListener; import in.andres.kandroid.kanboard.events.OnGetMyProjectsListener; import in.andres.kandroid.kanboard.events.OnGetOverdueTasksByProjectListener; import in.andres.kandroid.kanboard.events.OnGetProjectByIdListener; import in.andres.kandroid.kanboard.events.OnGetProjectUsersListener; import in.andres.kandroid.kanboard.events.OnGetSwimlaneListener; import in.andres.kandroid.kanboard.events.OnGetTaskListener; import in.andres.kandroid.kanboard.events.OnGetVersionListener; import in.andres.kandroid.kanboard.events.OnOpenTaskListener; import in.andres.kandroid.kanboard.events.OnRemoveCommentListener; import in.andres.kandroid.kanboard.events.OnRemoveSubtaskListener; import in.andres.kandroid.kanboard.events.OnRemoveTaskListener; import in.andres.kandroid.kanboard.events.OnSubtaskTimetrackingListener; import in.andres.kandroid.kanboard.events.OnUpdateCommentListener; import in.andres.kandroid.kanboard.events.OnUpdateSubtaskListener; import in.andres.kandroid.kanboard.events.OnUpdateTaskListener; @SuppressWarnings("unused") public class KanboardAPI { private class KanboardAsync extends AsyncTask<KanboardRequest, Void, KanboardResult> { @Override protected KanboardResult doInBackground(KanboardRequest... params) { HttpURLConnection con = null; int httpResponseCode = 0; List<JSONObject> responseList = new ArrayList<>(); for (String s : params[0].JSON) { try { Log.i(Constants.TAG, String.format("API: Send Request \"%s\"", params[0].Command)); if (BuildConfig.DEBUG) Log.v(Constants.TAG, String.format("API: Data:\n%s", s)); con = (HttpURLConnection) kanboardURL.openConnection(); if (con == null) return new KanboardResult(params[0], new JSONObject[] { new JSONObject( "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":0,\"message\":\"Unable to open connection\"},\"id\":null}") }, 0); con.setConnectTimeout(120000); con.setReadTimeout(120000); con.setInstanceFollowRedirects(false); con.setRequestMethod("POST"); con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("charset", "utf-8"); con.setDoOutput(true); con.setDoInput(true); DataOutputStream out = new DataOutputStream(con.getOutputStream()); out.writeBytes(s); out.flush(); out.close(); Log.i(Constants.TAG, String.format("API: HTTP Return Code for \"%s\": %d", params[0].Command, con.getResponseCode())); BufferedReader in; if (con.getResponseCode() < 400) in = new BufferedReader(new InputStreamReader(con.getInputStream())); else in = new BufferedReader(new InputStreamReader(con.getErrorStream())); String line; StringBuilder responseStr = new StringBuilder(); while ((line = in.readLine()) != null) { responseStr.append(line); } in.close(); httpResponseCode = con.getResponseCode(); con.disconnect(); Log.i(Constants.TAG, String.format("API: Received Response \"%s\"", params[0].Command)); if (BuildConfig.DEBUG) Log.v(Constants.TAG, String.format("API: Data:\n%s", responseStr.toString())); JSONObject response; try { response = new JSONObject(responseStr.toString()); } catch (JSONException e) { if (con.getResponseCode() >= 400) { response = new JSONObject(); response.put("jsonrpc", "2.0"); response.put("id", null); response.put("error", new JSONObject().put("code", con.getResponseCode()).put("message", con.getResponseMessage())); } else { e.printStackTrace(); response = null; } } responseList.add(response); } catch (UnknownHostException e) { Log.e(Constants.TAG, "API: Unknown Host."); e.printStackTrace(); try { responseList.add(new JSONObject( "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-10,\"message\":\"Unknown Host\"},\"id\":null}")); } catch (JSONException e1) { e1.printStackTrace(); } } catch (ProtocolException e) { Log.e(Constants.TAG, "API: Protocol Exception."); e.printStackTrace(); try { responseList.add(new JSONObject( "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-20,\"message\":\"Protocol Exception\"},\"id\":null}")); } catch (JSONException e1) { e1.printStackTrace(); } } catch (SocketTimeoutException e) { Log.e(Constants.TAG, String.format("API: Connection timed out.\tRequest: %s", params[0].Command)); e.printStackTrace(); try { responseList.add(new JSONObject( "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-30,\"message\":\"Network Timeout\"},\"id\":null}")); } catch (JSONException e1) { e1.printStackTrace(); } } catch (Exception e) { Log.e(Constants.TAG, String.format("API: CatchAll.\tRequest: %s", params[0].Command)); e.printStackTrace(); try { responseList.add(new JSONObject("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-1,\"message\":\"" + e.getMessage() + "\"},\"id\":null}\"")); } catch (JSONException e1) { e1.printStackTrace(); } } } return new KanboardResult(params[0], responseList.toArray(new JSONObject[] {}), httpResponseCode); } @Override protected void onPostExecute(KanboardResult s) { // Handle Errors if (s == null) { KanboardError res = new KanboardError(null, null, 0); // for (KanbordEvents l: listeners) // l.onError(res); for (OnErrorListener l : onErrorListeners) l.onError(res); return; } if (s.Result[0].has("error") || s.ReturnCode >= 400) { Log.e(Constants.TAG, s.Result[0].toString()); JSONObject err = s.Result[0].optJSONObject("error"); KanboardError res = new KanboardError(s.Request, err, s.ReturnCode); // for (KanbordEvents l: listeners) // l.onError(res); for (OnErrorListener l : onErrorListeners) l.onError(res); } // Handle Return Messages boolean success = false; if (s.Request.Command.equalsIgnoreCase("getMe")) { KanboardUserInfo res = null; try { if (s.Result[0].has("result") && (s.ReturnCode < 400)) { success = true; JSONObject jso = s.Result[0].getJSONObject("result"); res = new KanboardUserInfo(jso); } } catch (JSONException e) { e.printStackTrace(); } for (KanbordEvents l : listeners) l.onGetMe(success, res); for (OnGetMeListener l : onGetMeListeners) l.onGetMe(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getVersion")) { String res; int[] version = new int[3]; String tag = null; try { if (s.Result[0].has("result")) { success = true; res = s.Result[0].getString("result"); try { Pattern regex = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(?:,(.*)){0,1}$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Matcher regexMatcher = regex.matcher(res); if (regexMatcher.find()) { version[0] = Integer.parseInt(regexMatcher.group(1)); version[1] = Integer.parseInt(regexMatcher.group(2)); version[2] = Integer.parseInt(regexMatcher.group(3)); if (regexMatcher.groupCount() == 4 && regexMatcher.group(4) != null) tag = regexMatcher.group(4).trim(); } } catch (PatternSyntaxException ex) { ex.printStackTrace(); throw ex; // Syntax error in the regular expression } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetVersionListener l : onGetVersionListeners) l.onGetVersion(success, version, tag); return; } if (s.Request.Command.equalsIgnoreCase("getDefaultTaskColor")) { String res = null; try { if (s.Result[0].has("result")) { success = true; res = s.Result[0].getString("result"); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetDefaultColorListener l : onGetDefaultColorListeners) { l.onGetDefaultColor(success, res); } return; } if (s.Request.Command.equalsIgnoreCase("getDefaultTaskColors")) { Dictionary<String, KanboardColor> res = null; try { if (s.Result[0].has("result")) { success = true; res = new Hashtable<>(); JSONObject jso = s.Result[0].getJSONObject("result"); Iterator<String> iter = jso.keys(); while (iter.hasNext()) { String key = iter.next(); res.put(key, new KanboardColor(key, jso.getJSONObject(key))); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetDefaultColorsListener l : onGetDefaultColorsListeners) { l.onGetDefaultColors(success, res); } return; } if (s.Request.Command.equalsIgnoreCase("getMyProjectsList")) { List<KanboardProjectInfo> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONObject jso = s.Result[0].getJSONObject("result"); for (int i = 0; i < jso.names().length(); i++) { String key = jso.names().getString(i); res.add(new KanboardProjectInfo(Integer.parseInt(key), jso.optString(key, ""))); } } } catch (JSONException e) { e.printStackTrace(); } for (KanbordEvents l : listeners) l.onGetMyProjectsList(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getMyProjects")) { List<KanboardProject> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardProject(jsa.getJSONObject(i))); } } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetMyProjectsListener l : onGetMyProjectsListeners) l.onGetMyProjects(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getMyDashboard")) { KanboardDashboard res = null; try { if (s.Result[0].has("result")) { success = true; Object dash = s.Result[0].get("result"); res = new KanboardDashboard(dash); } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); for (OnErrorListener l : onErrorListeners) { try { l.onError(new KanboardError(s.Request, new JSONObject("{\"message\": \"\", \"code\": -50}"), 200)); } catch (JSONException e1) { e1.printStackTrace(); } } } // for (KanbordEvents l: listeners) // l.onGetMyDashboard(success, res); for (OnGetMyDashboardListener l : onGetMyDashboardListeners) l.onGetMyDashboard(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getMyActivityStream")) { List<KanboardActivity> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardActivity(jsa.getJSONObject(i))); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetMyActivityStreamListener l : onGetMyActivityStreamListeners) l.onGetMyActivityStream(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getMyOverdueTasks")) { List<KanboardTask> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardTask(jsa.getJSONObject(i))); } } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetMyOverdueTasksListener l : onGetMyOverdueTasksListeners) l.onGetMyOverdueTasks(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getProjectById")) { KanboardProject res = null; try { if (s.Result[0].has("result")) { success = true; res = new KanboardProject(s.Result[0].getJSONObject("result")); } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetProjectByIdListener l : onGetProjectByIdListeners) l.onGetProjectById(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getColumns")) { List<KanboardColumn> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardColumn(jsa.getJSONObject(i))); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetColumnsListener l : onGetColumnsListeners) l.onGetColumns(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getProjectUsers")) { Hashtable<Integer, String> res = null; try { if (s.Result[0].has("result")) { success = true; res = new Hashtable<>(); JSONObject jso = s.Result[0].getJSONObject("result"); Iterator<String> iter = jso.keys(); while (iter.hasNext()) { String key = iter.next(); res.put(Integer.parseInt(key), jso.getString(key)); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetProjectUsersListener l : onGetProjectUsersListeners) l.onGetProjectUsers(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getAllTasks")) { List<KanboardTask> res = null; int status = 0; try { if (s.Result[0].has("result")) { success = true; status = (new JSONObject(s.Request.JSON[0])).getJSONObject("params").getInt("status_id"); res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardTask(jsa.getJSONObject(i))); } } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetAllTasksListener l : onGetAllTasksListener) l.onGetAllTasks(success, status, res); return; } if (s.Request.Command.equalsIgnoreCase("getOverdueTasksByProject")) { List<KanboardTask> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardTask(jsa.getJSONObject(i))); } } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetOverdueTasksByProjectListener l : onGetOverdueTasksByProjectListeners) l.onGetOverdueTasksByProject(success, res); return; } if (s.Request.Command.equalsIgnoreCase("createTask")) { Integer res = null; try { if (!s.Result[0].getString("result").equalsIgnoreCase("false")) { success = true; res = s.Result[0].getInt("result"); } } catch (JSONException e) { e.printStackTrace(); } for (OnCreateTaskListener l : onCreateTaskListeners) l.onCreateTask(success, res); return; } if (s.Request.Command.equalsIgnoreCase("updateTask")) { success = s.Result[0].optBoolean("result", false); for (OnUpdateTaskListener l : onUpdateTaskListeners) l.onUpdateTask(success); return; } if (s.Request.Command.equalsIgnoreCase("getTask")) { KanboardTask res = null; try { if (s.Result[0].has("result")) { success = true; res = new KanboardTask(s.Result[0].getJSONObject("result")); } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (OnGetTaskListener l : onGetTaskListeners) l.onGetTask(success, res); return; } if (s.Request.Command.equalsIgnoreCase("openTask")) { success = s.Result[0].optBoolean("result", false); for (OnOpenTaskListener l : onOpenTaskListeners) l.onOpenTask(success); return; } if (s.Request.Command.equalsIgnoreCase("closeTask")) { success = s.Result[0].optBoolean("result", false); for (OnCloseTaskListener l : onCloseTaskListeners) l.onCloseTask(success); return; } if (s.Request.Command.equalsIgnoreCase("removeTask")) { success = s.Result[0].optBoolean("result", false); for (OnRemoveTaskListener l : onRemoveTaskListeners) l.onRemoveTask(success); return; } if (s.Request.Command.equalsIgnoreCase("getActiveSwimlanes")) { List<KanboardSwimlane> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardSwimlane(jsa.getJSONObject(i))); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetActiveSwimlanesListener l : onGetActiveSwimlanesListeners) l.onGetActiveSwimlanes(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getDefaultSwimlane")) { KanboardSwimlane res = null; try { if (s.Result[0].has("result")) { success = true; JSONObject jso = s.Result[0].getJSONObject("result"); res = new KanboardSwimlane(jso); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetSwimlaneListener l : onGetDefaultSwimlaneListeners) l.onGetSwimlane(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getSwimlane")) { KanboardSwimlane res = null; try { if (s.Result[0].has("result")) { success = true; res = new KanboardSwimlane(s.Result[0].getJSONObject("result")); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetSwimlaneListener l : onGetSwimlaneListeners) l.onGetSwimlane(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getAllCategories")) { List<KanboardCategory> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) { res.add(new KanboardCategory(jsa.getJSONObject(i))); } } } catch (JSONException e) { e.printStackTrace(); } for (OnGetAllCategoriesListener l : onGetAllCategoriesListeners) l.onGetAllCategories(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getCategory")) { KanboardCategory res = null; try { if (s.Result[0].has("result")) { success = true; res = new KanboardCategory(s.Result[0].getJSONObject("result")); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetCategoryListener l : onGetCategoryListeners) l.onGetCategory(success, res); return; } if (s.Request.Command.equalsIgnoreCase("createComment")) { Integer res = null; try { if (!s.Result[0].getString("result").equalsIgnoreCase("false")) { success = true; res = s.Result[0].getInt("result"); } } catch (JSONException e) { e.printStackTrace(); } for (OnCreateCommentListener l : onCreateCommentListeners) l.onCreateComment(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getAllComments")) { List<KanboardComment> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) res.add(new KanboardComment(jsa.getJSONObject(i))); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetAllCommentsListener l : onGetAllCommentsListeners) l.onGetAllComments(success, res); return; } if (s.Request.Command.equalsIgnoreCase("updateComment")) { success = s.Result[0].optBoolean("result", false); for (OnUpdateCommentListener l : onUpdateCommentListeners) l.onUpdateComment(success); return; } if (s.Request.Command.equalsIgnoreCase("removeComment")) { success = s.Result[0].optBoolean("result", false); for (OnRemoveCommentListener l : onRemoveCommentListeners) l.onRemoveComment(success); return; } if (s.Request.Command.equalsIgnoreCase("createSubtask")) { Integer res = null; try { if (!s.Result[0].getString("result").equalsIgnoreCase("false")) { success = true; res = s.Result[0].getInt("result"); } } catch (JSONException e) { e.printStackTrace(); } for (OnCreateSubtaskListener l : onCreateSubtaskListeners) l.onCreateSubtask(success, res); return; } if (s.Request.Command.equalsIgnoreCase("getAllSubtasks")) { List<KanboardSubtask> res = null; try { if (s.Result[0].has("result")) { success = true; res = new ArrayList<>(); JSONArray jsa = s.Result[0].getJSONArray("result"); for (int i = 0; i < jsa.length(); i++) res.add(new KanboardSubtask(jsa.getJSONObject(i))); } } catch (JSONException e) { e.printStackTrace(); } for (OnGetAllSubtasksListener l : onGetAllSubtasksListeners) l.onGetAllSubtasks(success, res); return; } if (s.Request.Command.equalsIgnoreCase("updateSubtask")) { success = s.Result[0].optBoolean("result", false); for (OnUpdateSubtaskListener l : onUpdateSubtaskListeners) l.onUpdateSubtask(success); return; } if (s.Request.Command.equalsIgnoreCase("removeSubtask")) { success = s.Result[0].optBoolean("result", false); for (OnRemoveSubtaskListener l : onRemoveSubtaskListeners) l.onRemoveSubtask(success); return; } if (s.Request.Command.equalsIgnoreCase("hasSubtaskTimer")) { try { hasSubtaskTimerSet.remove(new JSONObject(s.Request.JSON[0]).getJSONArray("params").getInt(0)); success = s.Result[0].optBoolean("result", false); } catch (JSONException e) { e.printStackTrace(); } if (s.Request.Listener != null) ((OnSubtaskTimetrackingListener) s.Request.Listener).onSubtaskTimetracking(success, 0); return; } if (s.Request.Command.equalsIgnoreCase("setSubtaskStartTime")) { success = s.Result[0].optBoolean("result", false); if (s.Request.Listener != null) ((OnSubtaskTimetrackingListener) s.Request.Listener).onSubtaskTimetracking(success, 0); return; } if (s.Request.Command.equalsIgnoreCase("setSubtaskEndTime")) { success = s.Result[0].optBoolean("result", false); if (s.Request.Listener != null) ((OnSubtaskTimetrackingListener) s.Request.Listener).onSubtaskTimetracking(success, 0); return; } if (s.Request.Command.equalsIgnoreCase("getSubtaskTimeSpent")) { double time; try { getSubtaskTimeSpentSet .remove(new JSONObject(s.Request.JSON[0]).getJSONArray("params").getInt(0)); time = s.Result[0].getDouble("result"); success = true; } catch (JSONException e) { time = 0; success = false; } if (s.Request.Listener != null) ((OnSubtaskTimetrackingListener) s.Request.Listener).onSubtaskTimetracking(success, time); return; } if (s.Request.Command.equalsIgnoreCase("KD_getDashboard")) { KanboardDashboard res = null; try { if (s.Result[0].has("result") && s.Result[1].has("result") && s.Result[2].has("result")) { success = true; res = new KanboardDashboard(s.Result[0].getJSONObject("result"), s.Result[1].getJSONArray("result"), s.Result[2].getJSONArray("result")); } } catch (JSONException | MalformedURLException e) { e.printStackTrace(); } for (KanbordEvents l : listeners) l.onGetMyDashboard(success, res); return; } if (s.Request.Command.equalsIgnoreCase("KD_getProjectById")) { KanboardProject res = null; try { if (s.Result[0].has("result") && s.Result[1].has("result") && s.Result[2].has("result") && s.Result[3].has("result") && s.Result[4].has("result") && s.Result[5].has("result") && s.Result[6].has("result")) { success = true; res = new KanboardProject(s.Result[0].optJSONObject("result"), s.Result[1].optJSONArray("result"), s.Result[2].optJSONArray("result"), s.Result[3].optJSONArray("result"), s.Result[4].optJSONArray("result"), s.Result[5].optJSONArray("result"), s.Result[6].optJSONArray("result"), s.Result[7].optJSONObject("result")); } } catch (MalformedURLException e) { e.printStackTrace(); } for (KanbordEvents l : listeners) l.onGetProjectById(success, res); return; } } } private URL kanboardURL; private ThreadPoolExecutor threadPoolExecutor; private HashSet<KanbordEvents> listeners = new HashSet<>(); private HashSet<OnGetMyDashboardListener> onGetMyDashboardListeners = new HashSet<>(); private HashSet<OnGetMyActivityStreamListener> onGetMyActivityStreamListeners = new HashSet<>(); private HashSet<OnGetMyOverdueTasksListener> onGetMyOverdueTasksListeners = new HashSet<>(); private HashSet<OnGetProjectByIdListener> onGetProjectByIdListeners = new HashSet<>(); private HashSet<OnGetColumnsListener> onGetColumnsListeners = new HashSet<>(); private HashSet<OnGetAllCommentsListener> onGetAllCommentsListeners = new HashSet<>(); private HashSet<OnGetAllTasksListener> onGetAllTasksListener = new HashSet<>(); private HashSet<OnGetOverdueTasksByProjectListener> onGetOverdueTasksByProjectListeners = new HashSet<>(); private HashSet<OnGetTaskListener> onGetTaskListeners = new HashSet<>(); private HashSet<OnGetAllCategoriesListener> onGetAllCategoriesListeners = new HashSet<>(); private HashSet<OnGetCategoryListener> onGetCategoryListeners = new HashSet<>(); private HashSet<OnGetProjectUsersListener> onGetProjectUsersListeners = new HashSet<>(); private HashSet<OnGetSwimlaneListener> onGetSwimlaneListeners = new HashSet<>(); private HashSet<OnGetActiveSwimlanesListener> onGetActiveSwimlanesListeners = new HashSet<>(); private HashSet<OnGetSwimlaneListener> onGetDefaultSwimlaneListeners = new HashSet<>(); private HashSet<OnGetAllSubtasksListener> onGetAllSubtasksListeners = new HashSet<>(); private HashSet<OnGetMeListener> onGetMeListeners = new HashSet<>(); private HashSet<OnCreateCommentListener> onCreateCommentListeners = new HashSet<>(); private HashSet<OnUpdateCommentListener> onUpdateCommentListeners = new HashSet<>(); private HashSet<OnRemoveCommentListener> onRemoveCommentListeners = new HashSet<>(); private HashSet<OnCreateSubtaskListener> onCreateSubtaskListeners = new HashSet<>(); private HashSet<OnUpdateSubtaskListener> onUpdateSubtaskListeners = new HashSet<>(); private HashSet<OnRemoveSubtaskListener> onRemoveSubtaskListeners = new HashSet<>(); private HashSet<OnOpenTaskListener> onOpenTaskListeners = new HashSet<>(); private HashSet<OnCloseTaskListener> onCloseTaskListeners = new HashSet<>(); private HashSet<OnRemoveTaskListener> onRemoveTaskListeners = new HashSet<>(); private HashSet<OnCreateTaskListener> onCreateTaskListeners = new HashSet<>(); private HashSet<OnUpdateTaskListener> onUpdateTaskListeners = new HashSet<>(); private HashSet<OnGetVersionListener> onGetVersionListeners = new HashSet<>(); private HashSet<OnErrorListener> onErrorListeners = new HashSet<>(); private HashSet<OnGetDefaultColorListener> onGetDefaultColorListeners = new HashSet<>(); private HashSet<OnGetDefaultColorsListener> onGetDefaultColorsListeners = new HashSet<>(); private HashSet<OnGetMyProjectsListener> onGetMyProjectsListeners = new HashSet<>(); //HashSets for AsyncTask limiting private HashSet<Integer> hasSubtaskTimerSet = new HashSet<>(); private HashSet<Integer> getSubtaskTimeSpentSet = new HashSet<>(); public KanboardAPI(String serverURL, final String username, final String password) throws IOException { Authenticator.setDefault(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password.toCharArray()); } }); serverURL = serverURL.trim(); String tmpURL = serverURL; if (!serverURL.endsWith("jsonrpc.php")) { if (!serverURL.endsWith("/")) tmpURL += "/"; tmpURL += "jsonrpc.php"; } kanboardURL = new URL(tmpURL); Log.i(Constants.TAG, String.format("Host uses %s", kanboardURL.getProtocol())); // threadPoolExecutor = new ThreadPoolExecutor(12, 12, 20, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(256)); threadPoolExecutor = (ThreadPoolExecutor) AsyncTask.THREAD_POOL_EXECUTOR; threadPoolExecutor.setCorePoolSize(12); threadPoolExecutor.setMaximumPoolSize(12); } // Event Listeners public void addListener(@NonNull KanbordEvents listener) { listeners.add(listener); } public void removeListener(@NonNull KanbordEvents listener) { listeners.remove(listener); } public void addOnGetMyDashboardListener(@NonNull OnGetMyDashboardListener listener) { onGetMyDashboardListeners.add(listener); } public void removeOnGetMyDashboardListener(@NonNull OnGetMyDashboardListener listener) { onGetMyDashboardListeners.remove(listener); } public void addOnGetMyActivityStreamListener(@NonNull OnGetMyActivityStreamListener listener) { onGetMyActivityStreamListeners.add(listener); } public void removeOnGetMyActivityStreamListener(@NonNull OnGetMyActivityStreamListener listener) { onGetMyActivityStreamListeners.remove(listener); } public void addOnGetMyOverdueTasksListener(@NonNull OnGetMyOverdueTasksListener listener) { onGetMyOverdueTasksListeners.add(listener); } public void removeOnGetMyOverdueTasksListener(@NonNull OnGetMyOverdueTasksListener listener) { onGetMyOverdueTasksListeners.remove(listener); } public void addOnGetProjectByIdListener(@NonNull OnGetProjectByIdListener listener) { onGetProjectByIdListeners.add(listener); } public void removeOnGetProjectByIdListener(@NonNull OnGetProjectByIdListener listener) { onGetProjectByIdListeners.remove(listener); } public void addOnGetColumnsListener(@NonNull OnGetColumnsListener listener) { onGetColumnsListeners.add(listener); } public void removeOnGetColumnsListener(@NonNull OnGetColumnsListener listener) { onGetColumnsListeners.remove(listener); } public void addOnGetAllCommentsListener(@NonNull OnGetAllCommentsListener listener) { onGetAllCommentsListeners.add(listener); } public void removeOnGetAllCommentsListener(@NonNull OnGetAllCommentsListener listener) { onGetAllCommentsListeners.remove(listener); } public void addOnGetActiveSwimlanesListener(@NonNull OnGetActiveSwimlanesListener listener) { onGetActiveSwimlanesListeners.add(listener); } public void removeOnGetActiveSwimlanesListener(@NonNull OnGetActiveSwimlanesListener listener) { onGetActiveSwimlanesListeners.remove(listener); } public void addOnGetSwimlaneListener(@NonNull OnGetSwimlaneListener listener) { onGetSwimlaneListeners.add(listener); } public void removeOnGetSwimlaneListener(@NonNull OnGetSwimlaneListener listener) { onGetSwimlaneListeners.remove(listener); } public void addOnGetDefaultSwimlaneListener(@NonNull OnGetSwimlaneListener listener) { onGetDefaultSwimlaneListeners.add(listener); } public void removeOnGetDefaultSwimlaneListener(@NonNull OnGetSwimlaneListener listener) { onGetDefaultSwimlaneListeners.remove(listener); } public void addOnGetAllTasksListener(@NonNull OnGetAllTasksListener listener) { onGetAllTasksListener.add(listener); } public void removeGetAllTasksListener(@NonNull OnGetAllTasksListener listener) { onGetAllTasksListener.remove(listener); } public void addOnGetOverdueTasksByProjectListener(@NonNull OnGetOverdueTasksByProjectListener listener) { onGetOverdueTasksByProjectListeners.add(listener); } public void removeGetOverdueTasksByProjectListener(@NonNull OnGetOverdueTasksByProjectListener listener) { onGetOverdueTasksByProjectListeners.remove(listener); } public void addOnGetTaskListener(@NonNull OnGetTaskListener listener) { onGetTaskListeners.add(listener); } public void removeGetTaskListener(@NonNull OnGetTaskListener listener) { onGetTaskListeners.remove(listener); } public void addOnOpenTaskListener(@NonNull OnOpenTaskListener listener) { onOpenTaskListeners.add(listener); } public void removeOnOpenTaskListener(@NonNull OnOpenTaskListener listener) { onOpenTaskListeners.remove(listener); } public void addOnCloseTaskListener(@NonNull OnCloseTaskListener listener) { onCloseTaskListeners.add(listener); } public void removeOnCloseTaskListener(@NonNull OnCloseTaskListener listener) { onCloseTaskListeners.remove(listener); } public void addOnRemoveTaskListener(@NonNull OnRemoveTaskListener listener) { onRemoveTaskListeners.add(listener); } public void removeOnRemoveTaskListener(@NonNull OnRemoveTaskListener listener) { onRemoveTaskListeners.remove(listener); } public void addOnGetAllCategoriesListener(@NonNull OnGetAllCategoriesListener listener) { onGetAllCategoriesListeners.add(listener); } public void removeOnGetAllCategoriesListener(@NonNull OnGetAllCategoriesListener listener) { onGetAllCategoriesListeners.remove(listener); } public void addOnGetCategoryListener(@NonNull OnGetCategoryListener listener) { onGetCategoryListeners.add(listener); } public void removeOnGetCategoryListener(@NonNull OnGetCategoryListener listener) { onGetCategoryListeners.remove(listener); } public void addOnGetProjectUsersListener(@NonNull OnGetProjectUsersListener listener) { onGetProjectUsersListeners.add(listener); } public void removeOnGetProjectUsersListener(@NonNull OnGetProjectUsersListener listener) { onGetProjectUsersListeners.remove(listener); } public void addOnGetAllSubtasksListener(@NonNull OnGetAllSubtasksListener listener) { onGetAllSubtasksListeners.add(listener); } public void removeOnGetAllSubtasksListener(@NonNull OnGetAllSubtasksListener listener) { onGetAllSubtasksListeners.remove(listener); } public void addOnGetMeListener(@NonNull OnGetMeListener listener) { onGetMeListeners.add(listener); } public void removeOnGetMeListener(@NonNull OnGetMeListener listener) { onGetMeListeners.remove(listener); } public void addOnCreateCommentListener(@NonNull OnCreateCommentListener listener) { onCreateCommentListeners.add(listener); } public void removeOnCreateCommentListener(@NonNull OnCreateCommentListener listener) { onCreateCommentListeners.remove(listener); } public void addOnUpdateCommentListener(@NonNull OnUpdateCommentListener listener) { onUpdateCommentListeners.add(listener); } public void removeOnUpdateCommentListener(@NonNull OnUpdateCommentListener listener) { onUpdateCommentListeners.remove(listener); } public void addOnRemoveCommentListener(@NonNull OnRemoveCommentListener listener) { onRemoveCommentListeners.add(listener); } public void removeOnRemoveCommentListener(@NonNull OnRemoveCommentListener listener) { onRemoveCommentListeners.remove(listener); } public void addOnCreateSubtaskListener(@NonNull OnCreateSubtaskListener listener) { onCreateSubtaskListeners.add(listener); } public void removeOnCreateSubtaskListener(@NonNull OnCreateSubtaskListener listener) { onCreateSubtaskListeners.remove(listener); } public void addOnUpdateSubtaskListener(@NonNull OnUpdateSubtaskListener listener) { onUpdateSubtaskListeners.add(listener); } public void removeOnUpdateSubtaskListener(@NonNull OnUpdateSubtaskListener listener) { onUpdateSubtaskListeners.remove(listener); } public void addOnRemoveSubtaskListener(@NonNull OnRemoveSubtaskListener listener) { onRemoveSubtaskListeners.add(listener); } public void removeOnRemoveSubtaskListener(@NonNull OnRemoveSubtaskListener listener) { onRemoveSubtaskListeners.remove(listener); } public void addOnCreateTaskListener(@NonNull OnCreateTaskListener listener) { onCreateTaskListeners.add(listener); } public void removeOnCreateTaskListener(@NonNull OnCreateTaskListener listener) { onCreateTaskListeners.remove(listener); } public void addOnUpdateTaskListener(@NonNull OnUpdateTaskListener listener) { onUpdateTaskListeners.add(listener); } public void removeOnUpdateTaskListener(@NonNull OnUpdateTaskListener listener) { onUpdateTaskListeners.remove(listener); } public void addOnGetVersionListener(@NonNull OnGetVersionListener listener) { onGetVersionListeners.add(listener); } public void removeOnGetVersionListener(@NonNull OnGetVersionListener listener) { onGetVersionListeners.remove(listener); } public void addOnGetDefaultColorListener(@NonNull OnGetDefaultColorListener listener) { onGetDefaultColorListeners.add(listener); } public void removeOnGetDefaultColorListener(@NonNull OnGetDefaultColorListener listener) { onGetDefaultColorListeners.remove(listener); } public void addOnGetDefaultColorsListener(@NonNull OnGetDefaultColorsListener listener) { onGetDefaultColorsListeners.add(listener); } public void removeOnGetDefaultColorsListener(@NonNull OnGetDefaultColorsListener listener) { onGetDefaultColorsListeners.remove(listener); } public void addOnGetMyProjectsListener(@NonNull OnGetMyProjectsListener listener) { onGetMyProjectsListeners.add(listener); } public void removeOnGetMyProjectsListener(@NonNull OnGetMyProjectsListener listener) { onGetMyProjectsListeners.remove(listener); } public void addErrorListener(@NonNull OnErrorListener listener) { onErrorListeners.add(listener); } public void removeErrorListener(@NonNull OnErrorListener listener) { onErrorListeners.remove(listener); } // API Calls public void getMe() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMe()); } public void getVersion() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getVersion()); } public void getDefaultTaskColors() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getDefaultTaskColors()); } public void getDefaultTaskColor() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getDefaultTaskColor()); } public void getMyProjectsList() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMyProjectsList()); } public void getMyProjects() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMyProjects()); } public void getMyDashboard() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMyDashboard()); } public void getMyActivityStream() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMyActivityStream()); } public void getMyOverdueTasks() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getMyOverdueTasks()); } public void getProjectById(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getProjectById(projectid)); } public void getProjectUsers(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getProjectUsers(projectid)); } public void getColumns(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getColumns(projectid)); } public void getAllTasks(int projectid, int status) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getAllTasks(projectid, status)); } public void getOverdueTasksByProject(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getOverdueTasksByProject(projectid)); } public void createTask(@NonNull String title, int projectid, @Nullable String colorid, @Nullable Integer columnid, @Nullable Integer ownerid, @Nullable Integer creatorid, @Nullable Date duedate, @Nullable String description, @Nullable Integer categoryid, @Nullable Integer score, @Nullable Integer swimlaneid, @Nullable Integer priority, @Nullable Integer recurrencestatus, @Nullable Integer recurrencetrigger, @Nullable Integer recurrencefactor, @Nullable Integer recurrencetimeframe, @Nullable Integer recurrencebasedate, @Nullable String[] tags, @Nullable Date starteddate) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.createTask(title, projectid, colorid, columnid, ownerid, creatorid, duedate, description, categoryid, score, swimlaneid, priority, recurrencestatus, recurrencetrigger, recurrencefactor, recurrencetimeframe, recurrencebasedate, tags, starteddate)); } public void updateTask(int taskid, @NonNull String title, @Nullable String colorid, @Nullable Integer ownerid, @Nullable Date duedate, @Nullable String description, @Nullable Integer categoryid, @Nullable Integer score, @Nullable Integer priority, @Nullable Integer recurrencestatus, @Nullable Integer recurrencetrigger, @Nullable Integer recurrencefactor, @Nullable Integer recurrencetimeframe, @Nullable Integer recurrencebasedate, @Nullable String[] tags, @Nullable Date starteddate) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.updateTask(taskid, title, colorid, ownerid, duedate, description, categoryid, score, priority, recurrencestatus, recurrencetrigger, recurrencefactor, recurrencetimeframe, recurrencebasedate, tags, starteddate)); } public void getTask(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getTask(taskid)); } public void openTask(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.openTask(taskid)); } public void closeTask(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.closeTask(taskid)); } public void removeTask(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.removeTask(taskid)); } public void getAllComments(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getAllComments(taskid)); } public void createComment(int taskid, int userid, String comment) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.createComment(taskid, userid, comment)); } public void updateComment(int commentid, @NonNull String comment) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.updateComment(commentid, comment)); } public void removeComment(int commentid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.removeComment(commentid)); } public void getActiveSwimlanes(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getActiveSwimlanes(projectid)); } public void getDefaultSwimlane(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getDefaultSwimlane(projectid)); } public void getSwimlane(int swimlaneid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getSwimlane(swimlaneid)); } public void getAllCategories(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getAllCategories(projectid)); } public void getCategory(int categoryid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getCategrory(categoryid)); } public void getAllSubtasks(int taskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getAllSubtasks(taskid)); } public void createSubtask(int taskid, @NonNull String title, @Nullable Integer userid, @Nullable Integer timeestimated, @Nullable Integer timespent, @Nullable Integer status) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.createSubtask(taskid, title, userid, timeestimated, timespent, status)); } public void updateSubtask(int subtaskid, int taskid, @Nullable String title, @Nullable Integer userid, @Nullable Integer timeestimated, @Nullable Integer timespent, @Nullable Integer status) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.updateSubtask(subtaskid, taskid, title, userid, timeestimated, timespent, status)); } public void removeSubtask(int subtaskid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.removeSubtask(subtaskid)); } public void hasSubtaskTimer(int subtaskid, int userid, @NonNull OnSubtaskTimetrackingListener listener) { if (!hasSubtaskTimerSet.contains(subtaskid)) { hasSubtaskTimerSet.add(subtaskid); new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.hasSubtaskTimer(subtaskid, userid, listener)); } } public void setSubtaskStartTime(int subtaskid, int userid, @NonNull OnSubtaskTimetrackingListener listener) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.setSubtaskStartTime(subtaskid, userid, listener)); } public void setSubtaskEndTime(int subtaskid, int userid, @NonNull OnSubtaskTimetrackingListener listener) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.setSubtaskEndTime(subtaskid, userid, listener)); } public void getSubtaskTimeSpent(int subtaskid, int userid, @NonNull OnSubtaskTimetrackingListener listener) { if (!getSubtaskTimeSpentSet.contains(subtaskid)) { getSubtaskTimeSpentSet.add(subtaskid); new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.getSubtaskTimeSpent(subtaskid, userid, listener)); } } public void KB_getDashboard() { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.KD_getDashboard()); } public void KB_getProjectById(int projectid) { new KanboardAsync().executeOnExecutor(threadPoolExecutor, KanboardRequest.KD_getProjectById(projectid)); } // TODO: add API calls public static boolean StringToBoolean(String s) { return s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("1"); } @Nullable public static Integer parseColorString(String colorstring) { try { return Color.parseColor(colorstring); } catch (IllegalArgumentException e) { Pattern c = Pattern.compile("rgb *\\( *([0-9]+), *([0-9]+), *([0-9]+) *\\)"); Matcher m = c.matcher(colorstring); if (m.matches()) { return Color.rgb(Integer.valueOf(m.group(1)), // r Integer.valueOf(m.group(2)), // g Integer.valueOf(m.group(3))); // b } } return null; } }