Skip to content

Commit

Permalink
feat: Implement direct editing repo
Browse files Browse the repository at this point in the history
Signed-off-by: Álvaro Brey <[email protected]>
  • Loading branch information
AlvaroBrey committed Feb 16, 2023
1 parent b334e76 commit 091911a
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import com.nextcloud.android.sso.exceptions.TokenMismatchException;
import com.nextcloud.android.sso.exceptions.UnknownErrorException;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
import com.nextcloud.android.sso.model.SingleSignOnAccount;

import java.net.HttpURLConnection;
import java.util.LinkedList;
Expand Down Expand Up @@ -94,6 +95,7 @@
import it.niedermann.owncloud.notes.persistence.ApiProvider;
import it.niedermann.owncloud.notes.persistence.CapabilitiesClient;
import it.niedermann.owncloud.notes.persistence.CapabilitiesWorker;
import it.niedermann.owncloud.notes.persistence.DirectEditingRepository;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.shared.model.CategorySortingMethod;
Expand Down Expand Up @@ -760,11 +762,23 @@ public void onError(@NonNull Throwable t) {

@Override
public void onNoteClick(int position, View v) {
// TODO restore to previous behaviour, this is just for testing
final boolean hasCheckedItems = tracker.getSelection().size() > 0;
if (!hasCheckedItems) {
final var note = (Note) adapter.getItem(position);
startActivity(new Intent(getApplicationContext(), EditNoteActivity.class)
.putExtra(EditNoteActivity.PARAM_NOTE_ID, note.getId()));
// startActivity(new Intent(getApplicationContext(), EditNoteActivity.class)
// .putExtra(EditNoteActivity.PARAM_NOTE_ID, note.getId()));
try {
final SingleSignOnAccount account = SingleAccountHelper.getCurrentSingleSignOnAccount(getApplicationContext());
final DirectEditingRepository repository = DirectEditingRepository.getInstance(getApplicationContext());
final var supported = repository.isDirectEditingSupportedByServer(account).blockingGet();
Log.d(TAG, "onNoteClick: direct editing is supported by server: " + supported);
final var directEditingUrl = repository.getDirectEditingUrl(account, note).blockingGet();
Log.d(TAG, "onNoteClick: direct editing url: " + directEditingUrl);
} catch (NoCurrentAccountSelectedException |
NextcloudFilesAppAccountNotFoundException e) {
throw new RuntimeException(e);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand All @@ -39,11 +40,14 @@ public class ApiProvider {
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;
Expand Down Expand Up @@ -77,6 +81,15 @@ public synchronized NotesAPI getNotesAPI(@NonNull Context context, @NonNull Sing
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package it.niedermann.owncloud.notes.persistence

import android.app.Application
import android.content.Context
import com.nextcloud.android.sso.model.SingleSignOnAccount
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import it.niedermann.owncloud.notes.persistence.entity.Note
import it.niedermann.owncloud.notes.shared.model.ApiVersion
import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingInfo
import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingRequestBody

// TODO better error handling
class DirectEditingRepository private constructor(private val applicationContext: Context) {

private val apiProvider: ApiProvider by lazy { ApiProvider.getInstance() }
private val notesRepository: NotesRepository by lazy {
NotesRepository.getInstance(
applicationContext,
)
}

// TODO check for internet connection, check for directEditing supporting fileId as argument
fun isDirectEditingSupportedByServer(account: SingleSignOnAccount): Single<Boolean> {
val pathSingle = getNotesPath(account)
val textAvailableSingle = getDirectEditingInfo(account)
.map { it.editors.containsKey(SUPPORTED_EDITOR_ID) }

return Single.zip(pathSingle, textAvailableSingle) { path, textAvailable ->
path.isNotEmpty() && textAvailable
}
}

private fun getDirectEditingInfo(account: SingleSignOnAccount): Single<DirectEditingInfo> {
val filesAPI = apiProvider.getFilesAPI(applicationContext, account)
return Single.fromCallable {
val call = filesAPI.getDirectEditingInfo()
val response = call.execute()
response.body()?.ocs?.data
?: throw RuntimeException("No DirectEditingInfo available")
}.subscribeOn(Schedulers.io())
}

private fun getNotesPath(account: SingleSignOnAccount): Single<String> {
return Single.fromCallable {
val call = notesRepository.getServerSettings(account, ApiVersion.API_VERSION_1_0)
val response = call.execute()
response.body()?.notesPath ?: ""
}.subscribeOn(Schedulers.io())
}

fun getDirectEditingUrl(
account: SingleSignOnAccount,
note: Note,
): Single<String> {
return getNotesPath(account)
.flatMap { notesPath ->
val filesAPI = apiProvider.getFilesAPI(applicationContext, account)
Single.fromCallable {
val call =
filesAPI.getDirectEditingUrl(
DirectEditingRequestBody(
path = notesPath,
editorId = SUPPORTED_EDITOR_ID,
fileId = note.remoteId!!,
),
)
val response = call.execute()
response.body()?.ocs?.data?.url
?: throw RuntimeException("No url available")
}.subscribeOn(Schedulers.io())
}
}

companion object {
private const val SUPPORTED_EDITOR_ID = "text"

private var instance: DirectEditingRepository? = null

/**
* @param applicationContext The application context. Do NOT use a view context to prevent leaks.
*/
@JvmStatic
fun getInstance(applicationContext: Context): DirectEditingRepository {
require(applicationContext is Application)
if (instance == null) {
instance = DirectEditingRepository(applicationContext)
}
return instance!!
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package it.niedermann.owncloud.notes.persistence.sync

import it.niedermann.owncloud.notes.shared.model.OcsResponse
import it.niedermann.owncloud.notes.shared.model.OcsUrl
import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingInfo
import it.niedermann.owncloud.notes.shared.model.directediting.DirectEditingRequestBody
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface FilesAPI {
@GET("directEditing?format=json")
fun getDirectEditingInfo(): Call<OcsResponse<DirectEditingInfo>>

@POST("directEditing/open?format=json")
fun getDirectEditingUrl(@Body body: DirectEditingRequestBody): Call<OcsResponse<OcsUrl>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package it.niedermann.owncloud.notes.shared.model

import com.google.gson.annotations.Expose

data class OcsUrl(
@Expose
@JvmField
var url: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package it.niedermann.owncloud.notes.shared.model.directediting

import com.google.gson.annotations.Expose

data class DirectEditingCreator(
@Expose
val id: String,
@Expose
val editor: String,
@Expose
val name: String,
@Expose
val extension: String,
@Expose
val mimetype: String,
@Expose
val templates: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package it.niedermann.owncloud.notes.shared.model.directediting

import com.google.gson.annotations.Expose

/**
* Editor for direct editing data model
*/
data class DirectEditingEditor(
@Expose
val id: String,
@Expose
val name: String,
@Expose
val mimetypes: ArrayList<String>,
@Expose
val optionalMimetypes: ArrayList<String>,
@Expose
val secure: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package it.niedermann.owncloud.notes.shared.model.directediting

import com.google.gson.annotations.Expose

data class DirectEditingInfo(
@Expose
val editors: Map<String, DirectEditingEditor>,
@Expose
val creators: Map<String, DirectEditingCreator>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package it.niedermann.owncloud.notes.shared.model.directediting

import com.google.gson.annotations.Expose

data class DirectEditingRequestBody(
@Expose
val path: String,
@Expose
val editorId: String,
@Expose
val fileId: Long,
)

0 comments on commit 091911a

Please sign in to comment.