-
-
Notifications
You must be signed in to change notification settings - Fork 139
/
Copy pathApiProvider.java
158 lines (138 loc) · 7.06 KB
/
ApiProvider.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* Nextcloud Notes - Android Client
*
* SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.niedermann.owncloud.notes.persistence;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializer;
import com.nextcloud.android.sso.api.NextcloudAPI;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import java.util.Calendar;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import it.niedermann.owncloud.notes.persistence.sync.CapabilitiesDeserializer;
import it.niedermann.owncloud.notes.persistence.sync.FilesAPI;
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
import it.niedermann.owncloud.notes.persistence.sync.OcsAPI;
import it.niedermann.owncloud.notes.shared.model.ApiVersion;
import it.niedermann.owncloud.notes.shared.model.Capabilities;
import retrofit2.NextcloudRetrofitApiBuilder;
import retrofit2.Retrofit;
/**
* Since creating APIs via {@link Retrofit} uses reflection and {@link NextcloudAPI} <a href="https://github.com/nextcloud/Android-SingleSignOn/issues/120#issuecomment-540069990">is supposed to stay alive as long as possible</a>, those artifacts are going to be cached.
* They can be invalidated by using either {@link #invalidateAPICache()} for all or {@link #invalidateAPICache(SingleSignOnAccount)} for a specific {@link SingleSignOnAccount} and will be recreated when they are queried the next time.
*/
@WorkerThread
public class ApiProvider {
private static final String TAG = ApiProvider.class.getSimpleName();
private static final ApiProvider INSTANCE = new ApiProvider();
private static final String API_ENDPOINT_OCS = "/ocs/v2.php/cloud/";
private static final String API_ENDPOINT_FILES ="/ocs/v2.php/apps/files/api/v1/";
private static final Map<String, NextcloudAPI> API_CACHE = new ConcurrentHashMap<>();
private static final Map<String, OcsAPI> API_CACHE_OCS = new ConcurrentHashMap<>();
private static final Map<String, NotesAPI> API_CACHE_NOTES = new ConcurrentHashMap<>();
private static final Map<String, FilesAPI> API_CACHE_FILES = new ConcurrentHashMap<>();
public static ApiProvider getInstance() {
return INSTANCE;
}
private ApiProvider() {
// Singleton
}
/**
* An {@link OcsAPI} currently shares the {@link Gson} configuration with the {@link NotesAPI} and therefore divides all {@link Calendar} milliseconds by 1000 while serializing and multiplies values by 1000 during deserialization.
*/
public synchronized OcsAPI getOcsAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount) {
if (API_CACHE_OCS.containsKey(ssoAccount.name)) {
return API_CACHE_OCS.get(ssoAccount.name);
}
final var ocsAPI = new NextcloudRetrofitApiBuilder(getNextcloudAPI(context, ssoAccount), API_ENDPOINT_OCS).create(OcsAPI.class);
API_CACHE_OCS.put(ssoAccount.name, ocsAPI);
return ocsAPI;
}
/**
* In case the {@param preferredApiVersion} changes, call {@link #invalidateAPICache(SingleSignOnAccount)} or {@link #invalidateAPICache()} to make sure that this call returns a {@link NotesAPI} that uses the correct compatibility layer.
*/
public synchronized NotesAPI getNotesAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount, @Nullable ApiVersion preferredApiVersion) {
if (API_CACHE_NOTES.containsKey(ssoAccount.name)) {
return API_CACHE_NOTES.get(ssoAccount.name);
}
final var notesAPI = new NotesAPI(getNextcloudAPI(context, ssoAccount), preferredApiVersion);
API_CACHE_NOTES.put(ssoAccount.name, notesAPI);
return notesAPI;
}
public synchronized FilesAPI getFilesAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount) {
if (API_CACHE_FILES.containsKey(ssoAccount.name)) {
return API_CACHE_FILES.get(ssoAccount.name);
}
final var filesAPI = new NextcloudRetrofitApiBuilder(getNextcloudAPI(context, ssoAccount), API_ENDPOINT_FILES).create(FilesAPI.class);
API_CACHE_FILES.put(ssoAccount.name, filesAPI);
return filesAPI;
}
private synchronized NextcloudAPI getNextcloudAPI(@NonNull Context context, @NonNull SingleSignOnAccount ssoAccount) {
if (API_CACHE.containsKey(ssoAccount.name)) {
return API_CACHE.get(ssoAccount.name);
} else {
Log.v(TAG, "NextcloudRequest account: " + ssoAccount.name);
final var nextcloudAPI = new NextcloudAPI(context.getApplicationContext(), ssoAccount,
new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.registerTypeHierarchyAdapter(Calendar.class, (JsonSerializer<Calendar>) (src, typeOfSrc, ctx) -> new JsonPrimitive(src.getTimeInMillis() / 1_000))
.registerTypeHierarchyAdapter(Calendar.class, (JsonDeserializer<Calendar>) (src, typeOfSrc, ctx) -> {
final var calendar = Calendar.getInstance();
calendar.setTimeInMillis(src.getAsLong() * 1_000);
return calendar;
})
.registerTypeAdapter(Capabilities.class, new CapabilitiesDeserializer())
.create(), (e) -> {
invalidateAPICache(ssoAccount);
e.printStackTrace();
});
API_CACHE.put(ssoAccount.name, nextcloudAPI);
return nextcloudAPI;
}
}
/**
* Invalidates the API cache for the given {@param ssoAccount}
*
* @param ssoAccount the ssoAccount for which the API cache should be cleared.
*/
public synchronized void invalidateAPICache(@NonNull SingleSignOnAccount ssoAccount) {
Log.v(TAG, "Invalidating API cache for " + ssoAccount.name);
if (API_CACHE.containsKey(ssoAccount.name)) {
final var nextcloudAPI = API_CACHE.get(ssoAccount.name);
if (nextcloudAPI != null) {
nextcloudAPI.close();
}
API_CACHE.remove(ssoAccount.name);
}
API_CACHE_NOTES.remove(ssoAccount.name);
API_CACHE_OCS.remove(ssoAccount.name);
}
/**
* Invalidates the whole API cache for all accounts
*/
public synchronized void invalidateAPICache() {
for (final String key : API_CACHE.keySet()) {
Log.v(TAG, "Invalidating API cache for " + key);
if (API_CACHE.containsKey(key)) {
final var nextcloudAPI = API_CACHE.get(key);
if (nextcloudAPI != null) {
nextcloudAPI.close();
}
API_CACHE.remove(key);
}
}
API_CACHE_NOTES.clear();
API_CACHE_OCS.clear();
}
}