diff options
author | Stefan Niedermann <info@niedermann.it> | 2021-04-28 12:25:24 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2021-04-28 12:25:24 +0300 |
commit | 1c3b9a0d629901d2d1c47000dae11347b06c57c9 (patch) | |
tree | c88848039af050cf423c00e1af13ef95f85c7a95 /app/src/main/java | |
parent | 403cdecc8650af44ddec8b627826588e9c229142 (diff) |
#1167 Use retrofit also for capabilities endpoint
Diffstat (limited to 'app/src/main/java')
13 files changed, 170 insertions, 557 deletions
diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesClient.java index 9a08bc1e..b053227e 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesClient.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/CapabilitiesClient.java @@ -1,87 +1,38 @@ package it.niedermann.owncloud.notes.persistence; import android.content.Context; -import android.content.pm.PackageInfo; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; -import com.nextcloud.android.sso.aidl.NextcloudRequest; -import com.nextcloud.android.sso.api.AidlNetworkRequest; -import com.nextcloud.android.sso.api.Response; -import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException; +import com.google.gson.JsonParseException; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.model.SingleSignOnAccount; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.io.IOException; +import it.niedermann.owncloud.notes.persistence.sync.ApiProvider; import it.niedermann.owncloud.notes.shared.model.Capabilities; +import retrofit2.Response; + +import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; @WorkerThread public class CapabilitiesClient { - private static final String TAG = CapabilitiesClient.class.getSimpleName(); - - private static final int MIN_NEXTCLOUD_FILES_APP_VERSION_CODE = 30090000; - - protected static final String HEADER_KEY_IF_NONE_MATCH = "If-None-Match"; protected static final String HEADER_KEY_ETAG = "ETag"; - private static final String API_PATH = "/ocs/v2.php/cloud/capabilities"; - private static final String METHOD_GET = "GET"; - private static final String PARAM_KEY_FORMAT = "format"; - private static final String PARAM_VALUE_JSON = "json"; - - private static final Map<String, String> parameters = new HashMap<>(); - - static { - parameters.put(PARAM_KEY_FORMAT, PARAM_VALUE_JSON); - } - - public static Capabilities getCapabilities(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount, @Nullable String lastETag) throws Exception { - final NextcloudRequest.Builder requestBuilder = new NextcloudRequest.Builder() - .setMethod(METHOD_GET) - .setUrl(API_PATH) - .setParameter(parameters); - - final Map<String, List<String>> header = new HashMap<>(); - if (lastETag != null && !lastETag.isEmpty()) { - header.put(HEADER_KEY_IF_NONE_MATCH, Collections.singletonList('"' + lastETag + '"')); - requestBuilder.setHeader(header); - } - - final NextcloudRequest nextcloudRequest = requestBuilder.build(); - final StringBuilder result = new StringBuilder(); - + public static Capabilities getCapabilities(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount, @Nullable String lastETag) throws NextcloudHttpRequestFailedException, IOException { + final ApiProvider provider = new ApiProvider(context, ssoAccount); + final Response<Capabilities> response = provider.getOcsAPI().getCapabilities(lastETag).execute(); try { - Log.v(TAG, ssoAccount.name + " → " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " "); - final Response response = SSOClient.requestFilesApp(context.getApplicationContext(), ssoAccount, nextcloudRequest); - Log.v(TAG, "NextcloudRequest: " + nextcloudRequest.toString()); - - final BufferedReader rd = new BufferedReader(new InputStreamReader(response.getBody())); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - response.getBody().close(); - - String etag = null; - final AidlNetworkRequest.PlainHeader eTagHeader = response.getPlainHeader(HEADER_KEY_ETAG); - if (eTagHeader != null) { - etag = eTagHeader.getValue().replace("\"", ""); - } - - return new Capabilities(result.toString(), etag); - } catch (NullPointerException e) { - final PackageInfo pInfo = context.getApplicationContext().getPackageManager().getPackageInfo("com.nextcloud.client", 0); - if (pInfo.versionCode < MIN_NEXTCLOUD_FILES_APP_VERSION_CODE) { - throw new NextcloudFilesAppNotSupportedException(); + final Capabilities capabilities = response.body(); + capabilities.setETag(response.headers().get(HEADER_KEY_ETAG)); + return capabilities; + } catch (JsonParseException e) { + if (e.getCause() instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) e.getCause()).getStatusCode() == HTTP_UNAVAILABLE) { + throw (NextcloudHttpRequestFailedException) e.getCause(); } else { throw e; } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java deleted file mode 100644 index f5a8f189..00000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClient.java +++ /dev/null @@ -1,217 +0,0 @@ -package it.niedermann.owncloud.notes.persistence; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.nextcloud.android.sso.aidl.NextcloudRequest; -import com.nextcloud.android.sso.api.AidlNetworkRequest; -import com.nextcloud.android.sso.api.Response; -import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotSupportedException; -import com.nextcloud.android.sso.model.SingleSignOnAccount; - -import org.json.JSONObject; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import it.niedermann.owncloud.notes.persistence.entity.Note; -import it.niedermann.owncloud.notes.shared.model.ApiVersion; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NoteResponse; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NotesResponse; - -@SuppressWarnings("WeakerAccess") -@WorkerThread -public abstract class NotesClient { - - final static int MIN_NEXTCLOUD_FILES_APP_VERSION_CODE = 30090000; - private static final String TAG = NotesClient.class.getSimpleName(); - - protected final Context appContext; - - protected static final String GET_PARAM_KEY_PRUNE_BEFORE = "pruneBefore"; - - protected static final String HEADER_KEY_ETAG = "ETag"; - protected static final String HEADER_KEY_LAST_MODIFIED = "Last-Modified"; - protected static final String HEADER_KEY_CONTENT_TYPE = "Content-Type"; - protected static final String HEADER_KEY_IF_NONE_MATCH = "If-None-Match"; - protected static final String HEADER_KEY_X_NOTES_API_VERSIONS = "X-Notes-API-Versions"; - - protected static final String HEADER_VALUE_APPLICATION_JSON = "application/json"; - - protected static final String METHOD_GET = "GET"; - protected static final String METHOD_PUT = "PUT"; - protected static final String METHOD_POST = "POST"; - protected static final String METHOD_DELETE = "DELETE"; - - public static final String JSON_ID = "id"; - public static final String JSON_TITLE = "title"; - public static final String JSON_CONTENT = "content"; - public static final String JSON_FAVORITE = "favorite"; - public static final String JSON_CATEGORY = "category"; - public static final String JSON_ETAG = "etag"; - public static final String JSON_MODIFIED = "modified"; - - public static final ApiVersion[] SUPPORTED_API_VERSIONS = new ApiVersion[]{ - new ApiVersion(1, 0), - new ApiVersion(0, 2) - }; - - public static NotesClient newInstance(@Nullable ApiVersion preferredApiVersion, - @NonNull Context appContext) { - if (preferredApiVersion == null) { - Log.i(TAG, "apiVersion is null, using " + NotesClientV02.class.getSimpleName()); - return new NotesClientV02(appContext); - } else if (preferredApiVersion.compareTo(SUPPORTED_API_VERSIONS[0]) == 0) { - Log.i(TAG, "Using " + NotesClientV1.class.getSimpleName()); - return new NotesClientV1(appContext); - } else if (preferredApiVersion.compareTo(SUPPORTED_API_VERSIONS[1]) == 0) { - Log.i(TAG, "Using " + NotesClientV02.class.getSimpleName()); - return new NotesClientV02(appContext); - } - Log.w(TAG, "Unsupported API version " + preferredApiVersion + " - try using " + NotesClientV02.class.getSimpleName()); - return new NotesClientV02(appContext); - } - - @SuppressWarnings("WeakerAccess") - protected NotesClient(@NonNull Context appContext) { - this.appContext = appContext; - } - - /** - * Gets the list of notes from the server. - * - * @param ssoAccount Account to be used - * @param lastModified Last modified time of a former response (Unix timestamp in seconds!). All notes older than this time will be skipped. - * @param lastETag ETag of a former response. If nothing changed, the response will be 304 NOT MODIFIED. - * @return list of notes - * @throws Exception - */ - abstract NotesResponse getNotes(SingleSignOnAccount ssoAccount, Calendar lastModified, String lastETag) throws Exception; - - abstract NoteResponse createNote(SingleSignOnAccount ssoAccount, Note note) throws Exception; - - abstract NoteResponse editNote(SingleSignOnAccount ssoAccount, Note note) throws Exception; - - abstract void deleteNote(SingleSignOnAccount ssoAccount, long noteId) throws Exception; - - /** - * This entity class is used to return relevant data of the HTTP reponse. - */ - public static class ResponseData { - private final String content; - private final String etag; - private final String supportedApiVersions; - private final Calendar lastModified; - - ResponseData(@NonNull String content, String etag, @NonNull Calendar lastModified, @Nullable String supportedApiVersions) { - this.content = content; - this.etag = etag; - this.lastModified = lastModified; - this.supportedApiVersions = supportedApiVersions; - } - - public String getContent() { - return content; - } - - public String getETag() { - return etag; - } - - public Calendar getLastModified() { - return lastModified; - } - - public String getSupportedApiVersions() { - return this.supportedApiVersions; - } - } - - abstract protected String getApiPath(); - - /** - * Request-Method for POST, PUT with or without JSON-Object-Parameter - * - * @param target Filepath to the wanted function - * @param method GET, POST, DELETE or PUT - * @param parameter optional headers to be sent - * @param requestBody JSON Object which shall be transferred to the server. - * @param lastETag optional ETag of last response - * @return Body of answer - */ - protected ResponseData requestServer(SingleSignOnAccount ssoAccount, String target, String method, Map<String, String> parameter, JSONObject requestBody, String lastETag) throws Exception { - final NextcloudRequest.Builder requestBuilder = new NextcloudRequest.Builder() - .setMethod(method) - .setUrl(getApiPath() + target); - if (parameter != null) { - requestBuilder.setParameter(parameter); - } - - final Map<String, List<String>> header = new HashMap<>(); - if (requestBody != null) { - header.put(HEADER_KEY_CONTENT_TYPE, Collections.singletonList(HEADER_VALUE_APPLICATION_JSON)); - requestBuilder.setRequestBody(requestBody.toString()); - } - if (lastETag != null && !lastETag.isEmpty() && METHOD_GET.equals(method)) { - header.put(HEADER_KEY_IF_NONE_MATCH, Collections.singletonList('"' + lastETag + '"')); - requestBuilder.setHeader(header); - } - - final NextcloudRequest nextcloudRequest = requestBuilder.build(); - final StringBuilder result = new StringBuilder(); - - try { - Log.v(TAG, ssoAccount.name + " → " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " "); - Log.d(TAG, "NextcloudRequest: " + nextcloudRequest.toString()); - final Response response = SSOClient.requestFilesApp(appContext, ssoAccount, nextcloudRequest); - - final BufferedReader rd = new BufferedReader(new InputStreamReader(response.getBody())); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - response.getBody().close(); - - String etag = ""; - final AidlNetworkRequest.PlainHeader eTagHeader = response.getPlainHeader(HEADER_KEY_ETAG); - if (eTagHeader != null) { - etag = Objects.requireNonNull(eTagHeader.getValue()).replace("\"", ""); - } - - final Calendar lastModified = Calendar.getInstance(); - lastModified.setTimeInMillis(0); - final AidlNetworkRequest.PlainHeader lastModifiedHeader = response.getPlainHeader(HEADER_KEY_LAST_MODIFIED); - if (lastModifiedHeader != null) - lastModified.setTimeInMillis(Date.parse(lastModifiedHeader.getValue())); - Log.d(TAG, "ETag: " + etag + "; Last-Modified: " + lastModified + " (" + lastModified + ")"); - - String supportedApiVersions = null; - final AidlNetworkRequest.PlainHeader supportedApiVersionsHeader = response.getPlainHeader(HEADER_KEY_X_NOTES_API_VERSIONS); - if (supportedApiVersionsHeader != null) { - supportedApiVersions = "[" + Objects.requireNonNull(supportedApiVersionsHeader.getValue()) + "]"; - } - - // return these header fields since they should only be saved after successful processing the result! - return new ResponseData(result.toString(), etag, lastModified, supportedApiVersions); - } catch (NullPointerException e) { - final PackageInfo pInfo = appContext.getPackageManager().getPackageInfo("com.nextcloud.client", 0); - if (pInfo.versionCode < MIN_NEXTCLOUD_FILES_APP_VERSION_CODE) { - throw new NextcloudFilesAppNotSupportedException(); - } else { - throw e; - } - } - } -} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV02.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV02.java deleted file mode 100644 index 31a4de39..00000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV02.java +++ /dev/null @@ -1,63 +0,0 @@ -package it.niedermann.owncloud.notes.persistence; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.nextcloud.android.sso.model.SingleSignOnAccount; - -import org.json.JSONObject; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; - -import it.niedermann.owncloud.notes.persistence.entity.Note; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NoteResponse; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NotesResponse; - -@WorkerThread -public class NotesClientV02 extends NotesClient { - - private static final String API_PATH = "/index.php/apps/notes/api/v0.2/"; - - NotesClientV02(@NonNull Context appContext) { - super(appContext); - } - - NotesResponse getNotes(SingleSignOnAccount ssoAccount, Calendar lastModified, String lastETag) throws Exception { - final Map<String, String> parameter = new HashMap<>(); - parameter.put(GET_PARAM_KEY_PRUNE_BEFORE, Long.toString(lastModified == null ? 0 : lastModified.getTimeInMillis() / 1_000)); - return new NotesResponse(requestServer(ssoAccount, "notes", METHOD_GET, parameter, null, lastETag)); - } - - private NoteResponse putNote(SingleSignOnAccount ssoAccount, Note note, String path, String method) throws Exception { - JSONObject paramObject = new JSONObject(); - paramObject.accumulate(JSON_CONTENT, note.getContent()); - paramObject.accumulate(JSON_MODIFIED, note.getModified().getTimeInMillis() / 1_000); - paramObject.accumulate(JSON_FAVORITE, note.getFavorite()); - paramObject.accumulate(JSON_CATEGORY, note.getCategory()); - return new NoteResponse(requestServer(ssoAccount, path, method, null, paramObject, null)); - } - - @Override - NoteResponse createNote(SingleSignOnAccount ssoAccount, Note note) throws Exception { - return putNote(ssoAccount, note, "notes", METHOD_POST); - } - - @Override - NoteResponse editNote(SingleSignOnAccount ssoAccount, Note note) throws Exception { - return putNote(ssoAccount, note, "notes/" + note.getRemoteId(), METHOD_PUT); - } - - @Override - void deleteNote(SingleSignOnAccount ssoAccount, long noteId) throws Exception { - this.requestServer(ssoAccount, "notes/" + noteId, METHOD_DELETE, null, null, null); - } - - @Override - protected String getApiPath() { - return API_PATH; - } -} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java deleted file mode 100644 index 56661543..00000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/NotesClientV1.java +++ /dev/null @@ -1,64 +0,0 @@ -package it.niedermann.owncloud.notes.persistence; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.nextcloud.android.sso.model.SingleSignOnAccount; - -import org.json.JSONObject; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Map; - -import it.niedermann.owncloud.notes.persistence.entity.Note; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NoteResponse; -import it.niedermann.owncloud.notes.shared.model.ServerResponse.NotesResponse; - -@WorkerThread -public class NotesClientV1 extends NotesClient { - - private static final String API_PATH = "/index.php/apps/notes/api/v1/"; - - NotesClientV1(@NonNull Context appContext) { - super(appContext); - } - - NotesResponse getNotes(SingleSignOnAccount ssoAccount, Calendar lastModified, String lastETag) throws Exception { - final Map<String, String> parameter = new HashMap<>(); - parameter.put(GET_PARAM_KEY_PRUNE_BEFORE, Long.toString(lastModified == null ? 0 : lastModified.getTimeInMillis() / 1_000)); - return new NotesResponse(requestServer(ssoAccount, "notes", METHOD_GET, parameter, null, lastETag)); - } - - private NoteResponse putNote(SingleSignOnAccount ssoAccount, Note note, String path, String method) throws Exception { - JSONObject paramObject = new JSONObject(); - paramObject.accumulate(JSON_TITLE, note.getTitle()); - paramObject.accumulate(JSON_CONTENT, note.getContent()); - paramObject.accumulate(JSON_MODIFIED, note.getModified().getTimeInMillis() / 1_000); - paramObject.accumulate(JSON_FAVORITE, note.getFavorite()); - paramObject.accumulate(JSON_CATEGORY, note.getCategory()); - return new NoteResponse(requestServer(ssoAccount, path, method, null, paramObject, null)); - } - - @Override - NoteResponse createNote(SingleSignOnAccount ssoAccount, Note note) throws Exception { - return putNote(ssoAccount, note, "notes", METHOD_POST); - } - - @Override - NoteResponse editNote(SingleSignOnAccount ssoAccount, Note note) throws Exception { - return putNote(ssoAccount, note, "notes/" + note.getRemoteId(), METHOD_PUT); - } - - @Override - void deleteNote(SingleSignOnAccount ssoAccount, long noteId) throws Exception { - this.requestServer(ssoAccount, "notes/" + noteId, METHOD_DELETE, null, null, null); - } - - @Override - protected String getApiPath() { - return API_PATH; - } -} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SSOClient.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SSOClient.java index d5901496..9f28cfff 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/SSOClient.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/SSOClient.java @@ -1,24 +1,40 @@ package it.niedermann.owncloud.notes.persistence; import android.content.Context; +import android.graphics.Color; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; +import com.bumptech.glide.load.HttpException; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializer; import com.nextcloud.android.sso.aidl.NextcloudRequest; import com.nextcloud.android.sso.api.NextcloudAPI; import com.nextcloud.android.sso.api.Response; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; import com.nextcloud.android.sso.model.SingleSignOnAccount; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Type; import java.util.Calendar; import java.util.HashMap; import java.util.Map; +import it.niedermann.android.util.ColorUtil; +import it.niedermann.owncloud.notes.persistence.sync.CapabilitiesDeserializer; +import it.niedermann.owncloud.notes.shared.model.Capabilities; + +import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; + @SuppressWarnings("WeakerAccess") @WorkerThread public class SSOClient { @@ -27,10 +43,6 @@ public class SSOClient { private static final Map<String, NextcloudAPI> mNextcloudAPIs = new HashMap<>(); - public static Response requestFilesApp(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount, @NonNull NextcloudRequest nextcloudRequest) throws Exception { - return getNextcloudAPI(context.getApplicationContext(), ssoAccount).performNetworkRequestV2(nextcloudRequest); - } - public static NextcloudAPI getNextcloudAPI(Context appContext, SingleSignOnAccount ssoAccount) { if (mNextcloudAPIs.containsKey(ssoAccount.name)) { return mNextcloudAPIs.get(ssoAccount.name); @@ -45,6 +57,7 @@ public class SSOClient { calendar.setTimeInMillis(src.getAsLong() * 1_000); return calendar; }) + .registerTypeAdapter(Capabilities.class, new CapabilitiesDeserializer()) .create(), new NextcloudAPI.ApiConnectedListener() { @Override diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java index ed6dcd6c..5317ed33 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/entity/Account.java @@ -7,7 +7,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.room.ColumnInfo; import androidx.room.Entity; -import androidx.room.Ignore; import androidx.room.Index; import androidx.room.PrimaryKey; @@ -21,7 +20,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.NoSuchElementException; -import it.niedermann.owncloud.notes.persistence.NotesClient; import it.niedermann.owncloud.notes.shared.model.ApiVersion; import it.niedermann.owncloud.notes.shared.model.Capabilities; @@ -83,7 +81,7 @@ public class Account implements Serializable { final Collection<ApiVersion> supportedApiVersions = new HashSet<>(versionsArray.length()); for (int i = 0; i < versionsArray.length(); i++) { final ApiVersion parsedApiVersion = ApiVersion.of(versionsArray.getString(i)); - for (ApiVersion temp : NotesClient.SUPPORTED_API_VERSIONS) { + for (ApiVersion temp : ApiVersion.SUPPORTED_API_VERSIONS) { if (temp.compareTo(parsedApiVersion) == 0) { supportedApiVersions.add(parsedApiVersion); break; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/ApiProvider.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/ApiProvider.java index cbb0687b..538afd89 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/ApiProvider.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/ApiProvider.java @@ -16,16 +16,23 @@ import retrofit2.NextcloudRetrofitApiBuilder; */ public class ApiProvider { + private static final String API_ENDPOINT_OCS = "/ocs/v2.php/cloud/"; private static final String API_ENDPOINT_NOTES = "/index.php/apps/notes/api/v1/"; + private final OcsAPI ocsAPI; private final NotesAPI notesAPI; public ApiProvider(@NonNull Context appContext, @NonNull SingleSignOnAccount ssoAccount) { final NextcloudAPI nextcloudAPI = SSOClient.getNextcloudAPI(appContext, ssoAccount); + ocsAPI = new NextcloudRetrofitApiBuilder(nextcloudAPI, API_ENDPOINT_OCS).create(OcsAPI.class); notesAPI = new NextcloudRetrofitApiBuilder(nextcloudAPI, API_ENDPOINT_NOTES).create(NotesAPI.class); } public NotesAPI getNotesAPI() { return notesAPI; } + + public OcsAPI getOcsAPI() { + return ocsAPI; + } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java new file mode 100644 index 00000000..c9bf78da --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/CapabilitiesDeserializer.java @@ -0,0 +1,81 @@ +package it.niedermann.owncloud.notes.persistence.sync; + +import android.graphics.Color; +import android.util.Log; + +import com.bumptech.glide.load.HttpException; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; + +import java.lang.reflect.Type; + +import it.niedermann.android.util.ColorUtil; +import it.niedermann.owncloud.notes.shared.model.Capabilities; + +import static java.net.HttpURLConnection.HTTP_UNAVAILABLE; + +public class CapabilitiesDeserializer implements JsonDeserializer<Capabilities> { + + private static final String TAG = CapabilitiesDeserializer.class.getSimpleName(); + + private static final String JSON_OCS = "ocs"; + private static final String JSON_OCS_META = "meta"; + private static final String JSON_OCS_META_STATUSCODE = "statuscode"; + private static final String JSON_OCS_DATA = "data"; + private static final String JSON_OCS_DATA_CAPABILITIES = "capabilities"; + private static final String JSON_OCS_DATA_CAPABILITIES_NOTES = "notes"; + private static final String JSON_OCS_DATA_CAPABILITIES_NOTES_API_VERSION = "api_version"; + private static final String JSON_OCS_DATA_CAPABILITIES_THEMING = "theming"; + private static final String JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR = "color"; + private static final String JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR_TEXT = "color-text"; + + @Override + public Capabilities deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + final Capabilities response = new Capabilities(); + final JsonObject ocs = json.getAsJsonObject().getAsJsonObject(JSON_OCS); + if (ocs.has(JSON_OCS_META)) { + final JsonObject meta = ocs.getAsJsonObject(JSON_OCS_META); + if (meta.has(JSON_OCS_META_STATUSCODE)) { + if (meta.get(JSON_OCS_META_STATUSCODE).getAsInt() == HTTP_UNAVAILABLE) { + Log.i(TAG, "Capabilities Endpoint: This instance is currently in maintenance mode."); + throw new JsonParseException(new NextcloudHttpRequestFailedException(HTTP_UNAVAILABLE, new HttpException(HTTP_UNAVAILABLE))); + } + } + } + if (ocs.has(JSON_OCS_DATA)) { + final JsonObject data = ocs.getAsJsonObject(JSON_OCS_DATA); + if (data.has(JSON_OCS_DATA_CAPABILITIES)) { + final JsonObject capabilities = data.getAsJsonObject(JSON_OCS_DATA_CAPABILITIES); + if (capabilities.has(JSON_OCS_DATA_CAPABILITIES_NOTES)) { + final JsonObject notes = capabilities.getAsJsonObject(JSON_OCS_DATA_CAPABILITIES_NOTES); + if (notes.has(JSON_OCS_DATA_CAPABILITIES_NOTES_API_VERSION)) { + final JsonElement apiVersion = notes.get(JSON_OCS_DATA_CAPABILITIES_NOTES_API_VERSION); + response.setApiVersion(apiVersion.isJsonArray() ? apiVersion.toString() : null); + } + } + if (capabilities.has(JSON_OCS_DATA_CAPABILITIES_THEMING)) { + final JsonObject theming = capabilities.getAsJsonObject(JSON_OCS_DATA_CAPABILITIES_THEMING); + if (theming.has(JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR)) { + try { + response.setColor(Color.parseColor(ColorUtil.INSTANCE.formatColorToParsableHexString(theming.get(JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR).getAsString()))); + } catch (Exception e) { + e.printStackTrace(); + } + } + if (theming.has(JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR_TEXT)) { + try { + response.setTextColor(Color.parseColor(ColorUtil.INSTANCE.formatColorToParsableHexString(theming.get(JSON_OCS_DATA_CAPABILITIES_THEMING_COLOR_TEXT).getAsString()))); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + return response; + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/NextcloudAPI.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/NextcloudAPI.java deleted file mode 100644 index 9ddb5941..00000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/NextcloudAPI.java +++ /dev/null @@ -1,33 +0,0 @@ -package it.niedermann.owncloud.notes.persistence.sync; - - -import java.util.List; - -import it.niedermann.owncloud.notes.persistence.entity.Note; -import retrofit2.Call; -import retrofit2.http.Body; -import retrofit2.http.DELETE; -import retrofit2.http.GET; -import retrofit2.http.POST; -import retrofit2.http.PUT; -import retrofit2.http.Path; -import retrofit2.http.Query; - -/** - * @link <a href="https://deck.readthedocs.io/en/latest/API/">Deck REST API</a> - */ -public interface NextcloudAPI { - - @GET("notes") - Call<List<Note>> getNotes(@Query(value = "pruneBefore") long lastModified, @Query("If-None-Match") String lastETag); - - @POST("notes") - Call<Note> createNote(@Body Note note); - - @PUT("notes/{remoteId}") - Call<Note> editNote(@Body Note note, @Path("remoteId") long remoteId); - - @DELETE("notes/{remoteId}") - Call<Note> deleteNote(@Path("remoteId") long noteId); - -} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/OcsAPI.java b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/OcsAPI.java new file mode 100644 index 00000000..17bc3c4f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/sync/OcsAPI.java @@ -0,0 +1,16 @@ +package it.niedermann.owncloud.notes.persistence.sync; + + +import it.niedermann.owncloud.notes.shared.model.Capabilities; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Query; + +/** + * @link <a href="https://deck.readthedocs.io/en/latest/API/">Deck REST API</a> + */ +public interface OcsAPI { + + @GET("capabilities?format=json") + Call<Capabilities> getCapabilities(@Query("If-None-Match") String eTag); +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ApiVersion.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ApiVersion.java index b27afd7a..761d0dc3 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ApiVersion.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ApiVersion.java @@ -10,6 +10,11 @@ import java.util.regex.Pattern; public class ApiVersion implements Comparable<ApiVersion> { private static final Pattern NUMBER_EXTRACTION_PATTERN = Pattern.compile("[0-9]+"); + public static final ApiVersion[] SUPPORTED_API_VERSIONS = new ApiVersion[]{ + new ApiVersion(1, 0), + new ApiVersion(0, 2) + }; + private String originalVersion = "?"; private int major; private int minor; diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java index 1c1bed3d..5514a91b 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/Capabilities.java @@ -6,6 +6,7 @@ import android.util.Log; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.bumptech.glide.load.HttpException; import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException; @@ -38,12 +39,17 @@ public class Capabilities { private String apiVersion = null; @ColorInt - private Integer color = -16743735; + private int color = -16743735; @ColorInt - private Integer textColor = -16777216; + private int textColor = -16777216; @Nullable - private final String eTag; + private String eTag; + public Capabilities() { + + } + + @VisibleForTesting public Capabilities(@NonNull String response, @Nullable String eTag) throws NextcloudHttpRequestFailedException { this.eTag = eTag; final JSONObject ocs; @@ -92,6 +98,10 @@ public class Capabilities { } } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + public String getApiVersion() { return apiVersion; } @@ -101,14 +111,26 @@ public class Capabilities { return eTag; } - public Integer getColor() { + public void setETag(@Nullable String eTag) { + this.eTag = eTag; + } + + public int getColor() { return color; } - public Integer getTextColor() { + public void setColor(@ColorInt int color) { + this.color = color; + } + + public int getTextColor() { return textColor; } + public void setTextColor(@ColorInt int textColor) { + this.textColor = textColor; + } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ServerResponse.java b/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ServerResponse.java deleted file mode 100644 index dca53fb9..00000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/shared/model/ServerResponse.java +++ /dev/null @@ -1,103 +0,0 @@ -package it.niedermann.owncloud.notes.shared.model; - -import androidx.annotation.Nullable; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; - -import it.niedermann.owncloud.notes.persistence.NotesClient; -import it.niedermann.owncloud.notes.persistence.entity.Note; - -/** - * Provides entity classes for handling server responses with a single note ({@link NoteResponse}) or a list of notes ({@link NotesResponse}). - */ -public class ServerResponse { - - public static class NoteResponse extends ServerResponse { - public NoteResponse(NotesClient.ResponseData response) { - super(response); - } - - public Note getNote() throws JSONException { - return getNoteFromJSON(new JSONObject(getContent())); - } - } - - public static class NotesResponse extends ServerResponse { - public NotesResponse(NotesClient.ResponseData response) { - super(response); - } - - public List<Note> getNotes() throws JSONException { - List<Note> notesList = new ArrayList<>(); - JSONArray notes = new JSONArray(getContent()); - for (int i = 0; i < notes.length(); i++) { - JSONObject json = notes.getJSONObject(i); - notesList.add(getNoteFromJSON(json)); - } - return notesList; - } - } - - - private final NotesClient.ResponseData response; - - ServerResponse(NotesClient.ResponseData response) { - this.response = response; - } - - protected String getContent() { - return response == null ? null : response.getContent(); - } - - public String getETag() { - return response.getETag(); - } - - public Calendar getLastModified() { - return response.getLastModified(); - } - - @Nullable - public String getSupportedApiVersions() { - return response.getSupportedApiVersions(); - } - - Note getNoteFromJSON(JSONObject json) throws JSONException { - long remoteId = 0; - String title = ""; - String content = ""; - Calendar modified = null; - boolean favorite = false; - String category = ""; - String etag = null; - if (!json.isNull(NotesClient.JSON_ID)) { - remoteId = json.getLong(NotesClient.JSON_ID); - } - if (!json.isNull(NotesClient.JSON_TITLE)) { - title = json.getString(NotesClient.JSON_TITLE); - } - if (!json.isNull(NotesClient.JSON_CONTENT)) { - content = json.getString(NotesClient.JSON_CONTENT); - } - if (!json.isNull(NotesClient.JSON_MODIFIED)) { - modified = Calendar.getInstance(); - modified.setTimeInMillis(json.getLong(NotesClient.JSON_MODIFIED) * 1_000); - } - if (!json.isNull(NotesClient.JSON_FAVORITE)) { - favorite = json.getBoolean(NotesClient.JSON_FAVORITE); - } - if (!json.isNull(NotesClient.JSON_CATEGORY)) { - category = json.getString(NotesClient.JSON_CATEGORY); - } - if (!json.isNull(NotesClient.JSON_ETAG)) { - etag = json.getString(NotesClient.JSON_ETAG); - } - return new Note(remoteId, modified, title, content, category, favorite, etag); - } -} |