diff --git a/android/AkvoRSR/res/layout/activity_project_list.xml b/android/AkvoRSR/res/layout/activity_project_list.xml index 2ebfe4d6..b2e6c92f 100644 --- a/android/AkvoRSR/res/layout/activity_project_list.xml +++ b/android/AkvoRSR/res/layout/activity_project_list.xml @@ -150,26 +150,14 @@ android:visibility="gone" /> - \ No newline at end of file diff --git a/android/AkvoRSR/res/values/strings.xml b/android/AkvoRSR/res/values/strings.xml index 3289247a..67077575 100644 --- a/android/AkvoRSR/res/values/strings.xml +++ b/android/AkvoRSR/res/values/strings.xml @@ -54,6 +54,7 @@ Photo: %s Nothing to show with this filter. Nothing fetched yet. Use the refresh button to see your projects.\n\nIf you have a low-bandwidth connection you should turn on delayed picture fetching in settings first. + You are not connected to any projects.\n\nYou must use the web inteface to log in and request to be added to one first. Your Projects: Updates: Refresh progress diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/ProjectListActivity.java b/android/AkvoRSR/src/org/akvo/rsr/up/ProjectListActivity.java index 25c40750..d1722b59 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/ProjectListActivity.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/ProjectListActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -66,12 +66,18 @@ public class ProjectListActivity extends ActionBarActivity { private ListView mList; private TextView mEmptyText; private TextView mFirstTimeText; + private TextView mUnemployedText; private BroadcastReceiver broadRec; private Button searchButton; + + private boolean mEmployed; //False if user is not employed with any organisation @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + //employment can for now only change at login, so assign it for life of activity + mEmployed = SettingsUtil.getAuthUser(this).getOrgIds().size() > 0; + + super.onCreate(savedInstanceState); setContentView(R.layout.activity_project_list); projCountLabel = (TextView) findViewById(R.id.projcountlabel); @@ -83,6 +89,7 @@ protected void onCreate(Bundle savedInstanceState) { mList = (ListView) findViewById(R.id.list_projects); mEmptyText = (TextView) findViewById(R.id.list_empty_text); mFirstTimeText = (TextView) findViewById(R.id.first_time_text); + mUnemployedText = (TextView) findViewById(R.id.unemployed_text); mList.setOnItemClickListener(new OnItemClickListener() { @Override @@ -247,17 +254,25 @@ private void getData() { } if (count == 0) { //no records, but why? mList.setVisibility(View.GONE); + if (!mEmployed) { + mEmptyText.setVisibility(View.GONE); + mFirstTimeText.setVisibility(View.GONE); + mUnemployedText.setVisibility(View.VISIBLE); + } else if (searchString == null || searchString.length() == 0) { //must be empty DB mEmptyText.setVisibility(View.GONE); mFirstTimeText.setVisibility(View.VISIBLE); + mUnemployedText.setVisibility(View.GONE); } else { //too filtered mEmptyText.setVisibility(View.VISIBLE); mFirstTimeText.setVisibility(View.GONE); + mUnemployedText.setVisibility(View.GONE); } } else { mList.setVisibility(View.VISIBLE); mEmptyText.setVisibility(View.GONE); mFirstTimeText.setVisibility(View.GONE); + mUnemployedText.setVisibility(View.GONE); } //Populate list view ProjectListCursorAdapter projects = new ProjectListCursorAdapter(this, dataCursor); @@ -270,10 +285,14 @@ private void getData() { * starts the service fetching new project data */ private void startGetProjectsService() { + if (!mEmployed) { //TODO should disable menu choice instead + return; //fetch would fail + } + if (GetProjectDataService.isRunning(this)) { //TODO should disable menu choice instead return; //only one at a time } - + //TODO: disable menu choice //start a service Intent i = new Intent(this, GetProjectDataService.class); diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/domain/User.java b/android/AkvoRSR/src/org/akvo/rsr/up/domain/User.java index fd82fbe0..b292d37a 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/domain/User.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/domain/User.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -28,10 +28,12 @@ public class User { private String email; private String apiKey; private String orgId; - private Set publishedProjects; + private Set mOrgIds; + private Set publishedProjects; public User() { - publishedProjects = new HashSet(10); + mOrgIds = new HashSet(2); + publishedProjects = new HashSet(10); } public String getId() { @@ -90,15 +92,47 @@ public void setApiKey(String summary) { this.apiKey = summary; } - public Set getPublishedProjects() { - return publishedProjects; - } - - public void addPublishedProject(String id) { - this.publishedProjects.add(id); - } - - public void clearPublishedProjects() { - this.publishedProjects.clear(); - } + public Set getPublishedProjIds() { + return publishedProjects; + } + + public String getPublishedProjIdsString() { + String projlist = ""; + for (String id : publishedProjects) { + projlist += id + ","; + } + if (projlist.length() > 0) + projlist = projlist.substring(0, projlist.length()-1); + return projlist; + } + + public void addPublishedProjId(String id) { + this.publishedProjects.add(id); + } + + public void clearPublishedProjIds() { + this.publishedProjects.clear(); + } + + public Set getOrgIds() { + return mOrgIds; + } + + public String getOrgIdsString() { + String orglist = ""; + for (String id : mOrgIds) { + orglist += id + ","; + } + if (orglist.length() > 0) + orglist=orglist.substring(0, orglist.length()-1); + return orglist; + } + + public void addOrgId(String id) { + this.mOrgIds.add(id); + } + + public void clearOrgIds() { + this.mOrgIds.clear(); + } } diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/service/GetProjectDataService.java b/android/AkvoRSR/src/org/akvo/rsr/up/service/GetProjectDataService.java index 6c97b4a6..8f18aa0d 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/service/GetProjectDataService.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/service/GetProjectDataService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -30,12 +30,9 @@ import org.akvo.rsr.up.util.FileUtil; import org.akvo.rsr.up.util.SettingsUtil; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningServiceInfo; import android.app.IntentService; import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -55,18 +52,14 @@ public GetProjectDataService() { public static boolean isRunning(Context context) { return mRunning; - /* this solution uses an interface documented as intended for debug use - ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (GetProjectDataService.class.getName().equals(service.service.getClassName())) { - return true; - } - } - return false; - */ } + + /** + * Fetch data from server. + * TODO: Send the object type as a string in the broadcastProgress call so we can have that displayed as part of the progress bar. + */ @Override protected void onHandleIntent(Intent intent) { mRunning = true; @@ -77,38 +70,32 @@ protected void onHandleIntent(Intent intent) { String host = SettingsUtil.host(this); ad.open(); + User user = SettingsUtil.getAuthUser(this); try { try { - dl.fetchProjectList(this, - new URL(SettingsUtil.host(this) + - String.format(ConstantUtil.FETCH_PROJ_URL_PATTERN, - SettingsUtil.Read(this, "authorized_orgid")))); - broadcastProgress(0, 50, 100); + int i = 0; + int projects = user.getPublishedProjIds().size(); + //Iterate over projects instead of using a complex query URL, since it can take so long that the proxy times out + for (String id : user.getPublishedProjIds()) { + dl.fetchProject(this, + new URL(SettingsUtil.host(this) + + String.format(ConstantUtil.FETCH_PROJ_URL_PATTERN,id))); + broadcastProgress(0, i++, projects); + } if (mFetchCountries) { - // TODO: rarely changes, so only fetch countries if we never - // did that + // TODO: rarely changes, so only fetch countries if we never did that dl.fetchCountryList(this, new URL(SettingsUtil.host(this) + String.format(ConstantUtil.FETCH_COUNTRIES_URL))); } broadcastProgress(0, 100, 100); if (mFetchUpdates) { - // We only get published projects from that URL, - // so we need to iterate on them and get corresponding updates - Cursor c = ad.listAllProjects(); - try { - int i = 0; - while (c.moveToNext()) { - i++; - String projId = c.getString(c.getColumnIndex(RsrDbAdapter.PK_ID_COL)); - dl.fetchUpdateListRestApi(this, //TODO: use _extra for fewer fetches, as country and user is included - new URL(host + String.format(ConstantUtil.FETCH_UPDATE_URL_PATTERN, projId)) - ); - broadcastProgress(1, i, c.getCount()); - } - } finally { - if (c != null) - c.close(); + int j = 0; + for (String projId : user.getPublishedProjIds()) { + dl.fetchUpdateListRestApi(this, //TODO: use _extra for fewer fetches, as country and user data is included + new URL(host + String.format(ConstantUtil.FETCH_UPDATE_URL_PATTERN, projId)) + ); + broadcastProgress(1, j++, projects); } } @@ -120,13 +107,12 @@ protected void onHandleIntent(Intent intent) { errMsg = getResources().getString(R.string.errmsg_update_fetch_failed) + e.getMessage(); } - if (mFetchUsers) { + if (mFetchUsers) { //Remove this once we use the _extra update API // Fetch missing user data for authors of the updates. // This API requires authorization - User user = SettingsUtil.getAuthUser(this); String key = String.format(Locale.US, ConstantUtil.API_KEY_PATTERN, user.getApiKey(), user.getUsername()); - int j = 0; +// int k = 0; List orgIds = ad.getMissingUsersList(); for (String id : orgIds) { try { @@ -139,7 +125,7 @@ protected void onHandleIntent(Intent intent) { key), id ); - j++; +// k++; } catch (FileNotFoundException e) { // possibly because user is no longer active Log.w(TAG, "Cannot find user:" + id); diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/service/SignInService.java b/android/AkvoRSR/src/org/akvo/rsr/up/service/SignInService.java index 7c8da660..a68640bf 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/service/SignInService.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/service/SignInService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -57,7 +57,7 @@ protected void onHandleIntent(Intent intent) { //use project list to set projects visible RsrDbAdapter dba = new RsrDbAdapter(this); dba.open(); - dba.setVisibleProjects(user.getPublishedProjects()); + dba.setVisibleProjects(user.getPublishedProjIds()); dba.close(); } diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/service/SubmitProjectUpdateService.java b/android/AkvoRSR/src/org/akvo/rsr/up/service/SubmitProjectUpdateService.java index 0b285e1f..bb3b6e09 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/service/SubmitProjectUpdateService.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/service/SubmitProjectUpdateService.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) + * + * This file is part of Akvo RSR. + * + * Akvo RSR is free software: you can redistribute it and modify it under the terms of + * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, + * either version 3 of the License or any later version. + * + * Akvo RSR 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 Affero General Public License included with this program for more details. + * + * The full license text can also be seen at . + */ + package org.akvo.rsr.up.service; import org.akvo.rsr.up.domain.User; diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/util/ConstantUtil.java b/android/AkvoRSR/src/org/akvo/rsr/up/util/ConstantUtil.java index 7a3d6705..5c249c42 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/util/ConstantUtil.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/util/ConstantUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -33,12 +33,10 @@ public class ConstantUtil { public static final String PWD_URL = "/sign_in/"; public static final String AUTH_URL = "/auth/token/"; public static final String API_KEY_PATTERN = "&api_key=%s&username=%s"; -// public static final String POST_UPDATE_URL = "/api/v1/project_update/?format=xml"; public static final String POST_UPDATE_URL = "/rest/v1/project_update/?format=xml"; - public static final String FETCH_UPDATE_URL_PATTERN = "/rest/v1/project_update/?format=xml&limit=1000&project=%s"; // /api/v1/project_update/?format=xml&limit=0&project= -// public static final String VERIFY_UPDATE_PATTERN = "/api/v1/project_update/?format=xml&uuid=%s&limit=2"; + public static final String FETCH_UPDATE_URL_PATTERN = "/rest/v1/project_update/?format=xml&limit=1000&project=%s"; public static final String VERIFY_UPDATE_PATTERN = "/rest/v1/project_update/?format=xml&uuid=%s&limit=2"; - public static final String FETCH_PROJ_URL_PATTERN = "/api/v1/project/?format=xml&limit=0&partnerships__organisation=%s"; + public static final String FETCH_PROJ_URL_PATTERN = "/rest/v1/project_up/%s/?format=xml&image_thumb_name=up&width=100"; //ask for thumbnail size public static final String FETCH_COUNTRIES_URL = "/api/v1/country/?format=xml&limit=0"; public static final String FETCH_PROJ_COUNT_URL = "/api/v1/project/?format=xml&limit=0&partnerships__organisation=%s"; public static final String PROJECT_PATH_PATTERN = "/api/v1/project/%s/"; @@ -93,7 +91,8 @@ public class ConstantUtil { public static final String AUTH_USERNAME_KEY = "authorized_username"; public static final String AUTH_APIKEY_KEY = "authorized_apikey"; public static final String AUTH_USERID_KEY = "authorized_userid"; - public static final String AUTH_ORGID_KEY = "authorized_orgid"; + public static final String AUTH_ORGID_KEY = "authorized_orgid"; + public static final String AUTH_PROJID_KEY = "authorized_projid"; public static final String LOCAL_ID_KEY = "next_local_id"; /** diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/util/Downloader.java b/android/AkvoRSR/src/org/akvo/rsr/up/util/Downloader.java index 0737fe05..5fbbee28 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/util/Downloader.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/util/Downloader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -33,11 +33,13 @@ import org.akvo.rsr.up.R; import org.akvo.rsr.up.dao.RsrDbAdapter; +import org.akvo.rsr.up.domain.Project; import org.akvo.rsr.up.domain.Update; import org.akvo.rsr.up.domain.User; import org.akvo.rsr.up.xml.AuthHandler; import org.akvo.rsr.up.xml.CountryListHandler; import org.akvo.rsr.up.xml.OrganisationHandler; +import org.akvo.rsr.up.xml.ProjectExtraRestHandler; import org.akvo.rsr.up.xml.ProjectListHandler; import org.akvo.rsr.up.xml.UpdateExtraRestListHandler; import org.akvo.rsr.up.xml.UpdateRestHandler; @@ -64,7 +66,7 @@ * This class originally used only the API at //server/api/V1 * but is being migrated to use the //server/rest/v1. * Method status (lowest level only): - * Authorize() OLD + * Authorize() - Special API, updated for RSR V3 * postXmlUpdateStreaming() NEW (necessary for geolocated updates) * verifyUpdate() NEW * fetchcountryList() OLD - should be rolled into fetchUpdateListRestApi @@ -111,28 +113,39 @@ public FailedPostException(String string) { * @throws SAXException * @throws IOException */ - public void fetchProjectList(Context ctx, URL url) throws ParserConfigurationException, SAXException, IOException { + public void fetchProject(Context ctx, URL url) throws ParserConfigurationException, SAXException, IOException { - //Log.v(TAG, "Fetching project list from " + url); - - /* Get a SAXParser from the SAXPArserFactory. */ - SAXParserFactory spf = SAXParserFactory.newInstance(); - SAXParser sp = spf.newSAXParser(); - - /* Get the XMLReader of the SAXParser we created. */ - XMLReader xr = sp.getXMLReader(); - /* Create a new ContentHandler and apply it to the XML-Reader*/ - ProjectListHandler myProjectListHandler = new ProjectListHandler(new RsrDbAdapter(ctx)); - xr.setContentHandler(myProjectListHandler); - /* Parse the xml-data from our URL. */ - //TODO THIS MIGHT HANG, no timeout defined... - xr.parse(new InputSource(url.openStream())); - /* Parsing has finished. */ - - /* Check if anything went wrong. */ - err = myProjectListHandler.getError(); + User user = SettingsUtil.getAuthUser(ctx); + HttpRequest h = HttpRequest.get(url).connectTimeout(10000); //10 sec timeout + h.header("Authorization", "Token " + user.getApiKey()); //This API needs authorization + int code = h.code();//evaluation starts the exchange + String serverVersion = h.header(ConstantUtil.SERVER_VERSION_HEADER); + Date serverDate = new Date(h.date()); + if (code == 200) { + /* Get a SAXParser from the SAXPArserFactory. */ + SAXParserFactory spf = SAXParserFactory.newInstance(); + SAXParser sp = spf.newSAXParser(); + /* Get the XMLReader of the SAXParser we created. */ + XMLReader xr = sp.getXMLReader(); + /* Create a new ContentHandler and apply it to the XML-Reader*/ + ProjectExtraRestHandler myHandler = new ProjectExtraRestHandler(serverVersion); + xr.setContentHandler(myHandler); + /* Parse the xml-data from our URL. */ + xr.parse(new InputSource(h.stream())); + /* Parsing has finished. */ + Project proj = myHandler.getProject(); + if (proj != null){ + Log.i(TAG, "Fetched project #" + proj.getId()); + } + /* Check what went wrong. */ + err = myHandler.getError(); + } else { + //Vanilla case is 403 forbidden on an auth failure + Log.e(TAG, "Fetch update list HTTP error code:" + code); + Log.e(TAG, h.body()); + throw new IOException("Unexpected server response " + code); + } - Log.i(TAG, "Fetched " + myProjectListHandler.getCount() + " projects"); } @@ -640,7 +653,7 @@ public static boolean postXmlUpdateStreaming(String urlTemplate, Update update, //Just long+lat for location. We do not currently do reverse geocoding in the app. //Country used to be mandatory, but that was changed final String locationTemplate = "%s%s"; - final boolean simulateUnresolvedPost = false; + final boolean simulateUnresolvedPost = true; boolean allSent = false; try { @@ -814,6 +827,11 @@ static public void sendUpdate(Context ctx, String localId, boolean resolved; try { resolved = postXmlUpdateStreaming(urlTemplate, upd, sendImages, user, userAgent, prog); + if (resolved) { //remember new ID and status for this update + upd.setUnsent(false);//TODO: this fails if verifyUpdate worked + upd.setDraft(false); + dba.updateUpdateIdSent(upd, localId); + } } catch (FailedPostException e) { //did not happen, user should try again upd.setUnsent(false); upd.setDraft(true); @@ -827,9 +845,6 @@ static public void sendUpdate(Context ctx, String localId, } if (resolved) { //remember new ID and status for this update - upd.setUnsent(false); - upd.setDraft(false); - dba.updateUpdateIdSent(upd, localId); Log.i(TAG, "Sent update" + localId); return; } else { @@ -858,7 +873,8 @@ static public User authorize(URL url, String username, String password) throws P Map data = new HashMap(); data.put("username", username); data.put("password", password); - + data.put("handles_unemployed", "True"); + HttpRequest h = HttpRequest.post(url).form(data).connectTimeout(10000); //10 sec timeout int code = h.code(); if (code == 200) { diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/util/SettingsUtil.java b/android/AkvoRSR/src/org/akvo/rsr/up/util/SettingsUtil.java index 4fb25aa2..8b197aa9 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/util/SettingsUtil.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/util/SettingsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2014 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -70,38 +70,51 @@ public static String host(Context context) { //TODO move auth keys to ConstantUtil public static void signOut(Context c) { //destroy credentials - SettingsUtil.Write(c, "authorized_username", ""); - SettingsUtil.Write(c, "authorized_userid", ""); - SettingsUtil.Write(c, "authorized_orgid", ""); - SettingsUtil.Write(c, "authorized_apikey", ""); + SettingsUtil.Write(c, ConstantUtil.AUTH_USERNAME_KEY, ""); + SettingsUtil.Write(c, ConstantUtil.AUTH_USERID_KEY, ""); + SettingsUtil.Write(c, ConstantUtil.AUTH_ORGID_KEY, ""); + SettingsUtil.Write(c, ConstantUtil.AUTH_PROJID_KEY, ""); + SettingsUtil.Write(c, ConstantUtil.AUTH_APIKEY_KEY, ""); } public static void signIn(Context c, User user) { //save credentials - SettingsUtil.Write(c, "authorized_username", user.getUsername()); - SettingsUtil.Write(c, "authorized_userid", user.getId()); - SettingsUtil.Write(c, "authorized_orgid", user.getOrgId()); - SettingsUtil.Write(c, "authorized_apikey", user.getApiKey()); + SettingsUtil.Write(c, ConstantUtil.AUTH_USERNAME_KEY, user.getUsername()); + SettingsUtil.Write(c, ConstantUtil.AUTH_USERID_KEY, user.getId()); + SettingsUtil.Write(c, ConstantUtil.AUTH_ORGID_KEY, user.getOrgIdsString());//comma-separated list, possibly empty, never null + SettingsUtil.Write(c, ConstantUtil.AUTH_PROJID_KEY, user.getPublishedProjIdsString());//comma-separated list, possibly empty, never null + SettingsUtil.Write(c, ConstantUtil.AUTH_APIKEY_KEY, user.getApiKey()); } public static boolean haveCredentials(Context c) { - String u = SettingsUtil.Read(c, "authorized_username"); - String i = SettingsUtil.Read(c, "authorized_userid"); - String o = SettingsUtil.Read(c, "authorized_orgid"); - String k = SettingsUtil.Read(c, "authorized_apikey"); + String u = SettingsUtil.Read(c, ConstantUtil.AUTH_USERNAME_KEY); + String i = SettingsUtil.Read(c, ConstantUtil.AUTH_USERID_KEY); + String o = SettingsUtil.Read(c, ConstantUtil.AUTH_ORGID_KEY); + String p = SettingsUtil.Read(c, ConstantUtil.AUTH_PROJID_KEY); + String k = SettingsUtil.Read(c, ConstantUtil.AUTH_APIKEY_KEY); return u != null && !u.equals("") && i != null && !i.equals("") - && o != null && !o.equals("") + && o != null + && p != null && k != null && !k.equals(""); } public static User getAuthUser(Context c) { User user = new User(); - user.setUsername(SettingsUtil.Read(c, "authorized_username")); - user.setId(SettingsUtil.Read(c, "authorized_userid")); - user.setOrgId(SettingsUtil.Read(c, "authorized_orgid")); - user.setApiKey(SettingsUtil.Read(c, "authorized_apikey")); + user.setUsername(SettingsUtil.Read(c, ConstantUtil.AUTH_USERNAME_KEY)); + user.setId(SettingsUtil.Read(c, ConstantUtil.AUTH_USERID_KEY)); + String idstr = SettingsUtil.Read(c, ConstantUtil.AUTH_ORGID_KEY); + if (idstr != null) { + String ids[] = idstr.split(","); + for (String id : ids) user.addOrgId(id); + } + String projstr = SettingsUtil.Read(c, ConstantUtil.AUTH_PROJID_KEY); + if (projstr != null) { + String ids[] = projstr.split(","); + for (String id : ids) user.addPublishedProjId(id); + } + user.setApiKey(SettingsUtil.Read(c, ConstantUtil.AUTH_APIKEY_KEY)); return user; } diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/xml/AuthHandler.java b/android/AkvoRSR/src/org/akvo/rsr/up/xml/AuthHandler.java index 3bddbc99..ad0869ea 100644 --- a/android/AkvoRSR/src/org/akvo/rsr/up/xml/AuthHandler.java +++ b/android/AkvoRSR/src/org/akvo/rsr/up/xml/AuthHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Stichting Akvo (Akvo Foundation) + * Copyright (C) 2012-2015 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo RSR. * @@ -16,8 +16,6 @@ package org.akvo.rsr.up.xml; -import java.util.Set; - import org.akvo.rsr.up.domain.User; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -27,15 +25,17 @@ * Example input: * - asdjklfhlasufhkjasdjfnhalkjdnkjsdhfkjsdnkjfnsdfkjhsdkjfs - 666 - 42 + asdjklfhlasufhkjasdjfnhalkjdnkjsdhfkjsdnkjfnsdfkjhsdkjfs + 666 + 42 + 43 - 42 - 4711 - + 42 + 4711 + + From V3, we can get multiple org_id, and published_projects can be empty if user is not employed (yet). */ @@ -73,14 +73,6 @@ public boolean getError() { return syntaxError; } - public String getApiKey() { - return user.getApiKey(); - } - - public Set getPublishedProjects() { - return user.getPublishedProjects(); - } - public User getUser() { return user; } @@ -143,10 +135,11 @@ public void endElement(String namespaceURI, String localName, String qName) thro this.in_projects = false; } else if (in_projid && localName.equals("id")) { this.in_projid = false; - user.addPublishedProject(val); + user.addPublishedProjId(val); } else if (localName.equals("org_id")) { this.in_orgid = false; - user.setOrgId(val); + user.setOrgId(val); //backward compatibility + user.addOrgId(val); } } diff --git a/android/AkvoRSR/src/org/akvo/rsr/up/xml/ProjectExtraRestHandler.java b/android/AkvoRSR/src/org/akvo/rsr/up/xml/ProjectExtraRestHandler.java new file mode 100644 index 00000000..95eda9da --- /dev/null +++ b/android/AkvoRSR/src/org/akvo/rsr/up/xml/ProjectExtraRestHandler.java @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2015 Stichting Akvo (Akvo Foundation) + * + * This file is part of Akvo RSR. + * + * Akvo RSR is free software: you can redistribute it and modify it under the terms of + * the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, + * either version 3 of the License or any later version. + * + * Akvo RSR 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 Affero General Public License included with this program for more details. + * + * The full license text can also be seen at . + */ + +package org.akvo.rsr.up.xml; + +import org.akvo.rsr.up.domain.Project; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/* + * Parses output from the app-targeted REST API; One project with extra nested resource data; + * Example input: + * + + 2339 + 2014-10-03T11:23:162014-10-08T21:17:15 + WakaWaka + Testing a pay-as-you-go energy service in Rwanda using Akvo FLOW and RSR + + 636 + -25.1846546.085121 + Ambovombe + Androy + Ambovombe + 16 + 6 + + + + + A + WakaWaka will use Akvo FLOW and RSR in Rwanda to monitor the use of WakaWaka solar lights in a new pay-as-you-go energy service. + WakaWaka logo + The aim of this project is to support and train WakaWaka to use Akvo FLOW and RSR to monitor the WakaWaka Virtual Grid programme in Rwanda. + + September 2014 - Set up Akvo FLOW instance: An Akvo FLOW dashboard will be set up for WakaWaka. + Ongoing - Strategic partnership building: WakaWaka and Akvo would like to enrich their partnership on strategic level. + + Akvo is a self-sustaining organisation + WakaWaka is starting a new project called Virtual Grid. Virtual Grid is a pilot to test a pay-as-you-go energy service. The pilot will involve 9,000 households in Rwanda. In order to do an impact evaluation of this pilot, baseline and follow-up studies will be conducted. Akvo FLOW will be used to conduct the baseline study. Akvo RSR will be used to provide updates from the field. + + en + 0 + + EUR + 2014-09-22 + + 2015-09-22 + + False + + + + + + + + + + 15012.0015012.000.00 + + 10 + + + /media/db/project/2856/Project_2856_current_image_2015-03-27_14.21.04.jpg + + /media/cache/53/95/53954c937cc643bffeca011dfbef9ae4.jpg + + + + + + */ + + + +public class ProjectExtraRestHandler extends DefaultHandler { + + + // =========================================================== + // Fields + // =========================================================== + + private boolean in_project = false; + private boolean in_proj_id = false; + private boolean in_title = false; + private boolean in_subtitle = false; + private boolean in_summary = false; + private boolean in_funds = false; + private boolean in_primloc = false; + + private boolean in_current_image = false; + private boolean in_thumbnails = false; + private boolean in_thumbnail_url = false; + + private boolean in_loc_id = false; + private boolean in_country_id = false; + private boolean in_state = false; + private boolean in_city = false; + private boolean in_long = false; + private boolean in_lat = false; + + private Project mCurrentProj; + + private int depth = 0; + private boolean syntaxError = false; + private String buffer; //used to accumulate a tag content + + + /* + * constructor + */ + public ProjectExtraRestHandler(String serverVersion) { + super(); + } + // =========================================================== + // Getter & Setter + // =========================================================== + + public boolean getError() { + return syntaxError; + } + + public Project getProject() { + return mCurrentProj; + } + + // =========================================================== + // Methods + // =========================================================== + @Override + public void startDocument() throws SAXException { + depth = 0; + } + + @Override + public void endDocument() throws SAXException { + } + + /** Gets be called on opening tags like: + * + * Can provide attribute(s), when xml was like: + * */ + @Override + public void startElement(String namespaceURI, String localName, + String qName, Attributes atts) throws SAXException { + buffer = ""; + if (localName.equals("root") && depth == 0) { + this.in_project = true; + mCurrentProj = new Project(); + } else if (in_project) + if (localName.equals("id") && depth == 1) { + this.in_proj_id = true; + } else if (localName.equals("title") && depth == 1) { + this.in_title = true; + } else if (localName.equals("subtitle") && depth == 1) { + this.in_subtitle = true; + } else if (localName.equals("funds")) { + this.in_funds = true; + } else if (localName.equals("project_plan_summary") && depth==1) { + this.in_summary = true; + } else if (localName.equals("primary_location")) { + this.in_primloc = true; + } else if (localName.equals("id") && in_primloc) { + this.in_loc_id = true; + } else if (localName.equals("country") && in_primloc) { + this.in_country_id = true; + } else if (localName.equals("state") && in_primloc) { + this.in_state = true; + } else if (localName.equals("city") && in_primloc) { + this.in_city = true; + } else if (localName.equals("latitude") && in_primloc) { + this.in_lat = true; + } else if (localName.equals("longitude") && in_primloc) { + this.in_long = true; + } else if (localName.equals("current_image") && depth==1) { + this.in_current_image = true; + } else if (localName.equals("thumbnails") && in_current_image) { + this.in_thumbnails = true; + } else if (localName.equals("map_thumb") && in_thumbnails) { + this.in_thumbnail_url = true; + } + depth++; + } + + + /** Gets called on closing tags like: + * */ + @Override + public void endElement(String namespaceURI, String localName, String qName) + throws SAXException { + depth--; + if (localName.equals("id") && depth==1) { + this.in_proj_id= false; + mCurrentProj.setId(buffer); + } else if (localName.equals("title") && depth==1) { + this.in_title = false; + mCurrentProj.setTitle(buffer); + } else if (localName.equals("subtitle") && depth==1) { + this.in_subtitle = false; + mCurrentProj.setSubtitle(buffer); + } else if (localName.equals("funds")) { + this.in_funds = false; + try { + mCurrentProj.setFunds(Double.parseDouble(buffer)); + } catch (NumberFormatException e) { + syntaxError = true; + } + } else if (localName.equals("primary_location")) { + this.in_primloc = false; + } else if (localName.equals("root") && depth==0) { + this.in_project = false; + } else if (localName.equals("project_plan_summary") && depth==1) { + this.in_summary = false; + mCurrentProj.setSummary(buffer); + } else if (localName.equals("current_image") && depth==1) { + this.in_current_image = false; + } else if (localName.equals("id") && in_primloc) { + this.in_loc_id= false; + } else if (localName.equals("country") && in_primloc) { + this.in_country_id = false; + mCurrentProj.setCountry(buffer); + } else if (localName.equals("state") && in_primloc) { + this.in_state = false; + mCurrentProj.setState(buffer); + } else if (localName.equals("city") && in_primloc) { + this.in_city = false; + mCurrentProj.setCity(buffer); + } else if (localName.equals("latitude") && in_primloc) { + this.in_lat = false; + mCurrentProj.setLatitude(buffer); + } else if (localName.equals("longitude") && in_primloc) { + this.in_long = false; + mCurrentProj.setLongitude(buffer); + } else if (localName.equals("thumbnails") && in_current_image) { + this.in_thumbnails = false; + } else if (localName.equals("map_thumb") && in_thumbnails) { + this.in_thumbnail_url = false; + mCurrentProj.setThumbnailUrl(buffer); + } + } + + + /** Gets called on the following structure: + * characters */ + @Override + public void characters(char ch[], int start, int length) { + if (mCurrentProj != null) { + if (this.in_proj_id || + this.in_summary || + this.in_thumbnail_url || + this.in_title || + this.in_subtitle || + this.in_country_id || + this.in_state || + this.in_city || + this.in_funds || + this.in_loc_id || + this.in_long || + this.in_lat + ) { + buffer += new String(ch, start, length); + } + } else + syntaxError = true; //set error flag + } + +}