Skip to content

Commit

Permalink
Support opening new notes with direct editing
Browse files Browse the repository at this point in the history
Signed-off-by: Álvaro Brey <[email protected]>
  • Loading branch information
AlvaroBrey committed Mar 6, 2023
1 parent 5dfe0dd commit 1bf7cc9
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
import com.nextcloud.android.sso.model.SingleSignOnAccount;

import java.io.BufferedReader;
import java.io.IOException;
Expand Down Expand Up @@ -124,9 +125,20 @@ private long getNoteId() {
}

private long getAccountId() {
return getIntent().getLongExtra(PARAM_ACCOUNT_ID, 0);
final long idParam = getIntent().getLongExtra(PARAM_ACCOUNT_ID, 0);
if (idParam == 0) {
try {
final SingleSignOnAccount ssoAcc = SingleAccountHelper.getCurrentSingleSignOnAccount(this);
return repo.getAccountByName(ssoAcc.name).getId();
} catch (NextcloudFilesAppAccountNotFoundException |
NoCurrentAccountSelectedException e) {
Log.w(TAG, "getAccountId: no current account", e);
}
}
return idParam;
}


/**
* Starts the note fragment for an existing note or a new note.
* The actual behavior is triggered by the activity's intent.
Expand Down Expand Up @@ -174,23 +186,28 @@ private void launchExistingNote(long accountId, long noteId, @Nullable final Str
savedState = getSupportFragmentManager().saveFragmentInstanceState(fragment);
}
fragment = getNoteFragment(accountId, noteId, mode);

if (savedState != null) {
fragment.setInitialSavedState(savedState);
}
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit();
if (!fragment.shouldShowToolbar()) {
binding.toolbar.setVisibility(View.GONE);
} else {
binding.toolbar.setVisibility(View.VISIBLE);
}
replaceFragment();
});
}

private void replaceFragment() {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit();
if (!fragment.shouldShowToolbar()) {
binding.toolbar.setVisibility(View.GONE);
} else {
binding.toolbar.setVisibility(View.VISIBLE);
}
}


/**
* Returns the preferred mode for the account. If the mode is "remember last" the last mode is returned.
* If the mode is "direct edit" and the account does not support direct edit, the default mode is returned.
*/
private String getPreferenceMode(long accountId) {
final Account accountById = repo.getAccountById(accountId);
final var directEditAvailable = accountById != null && accountById.isDirectEditingAvailable();

final var prefKeyNoteMode = getString(R.string.pref_key_note_mode);
final var prefKeyLastMode = getString(R.string.pref_key_last_note_mode);
Expand All @@ -207,8 +224,12 @@ private String getPreferenceMode(long accountId) {
effectiveMode = preferences.getString(prefKeyLastMode, defaultMode);
}

if (effectiveMode.equals(prefValueDirectEdit) && !directEditAvailable) {
effectiveMode = defaultMode;
if (effectiveMode.equals(prefValueDirectEdit)) {
final Account accountById = repo.getAccountById(accountId);
final var directEditAvailable = accountById != null && accountById.isDirectEditingAvailable();
if (!directEditAvailable) {
effectiveMode = defaultMode;
}
}

return effectiveMode;
Expand All @@ -233,6 +254,20 @@ private BaseNoteFragment getNoteFragment(long accountId, long noteId, final @Nul
}
}


@NonNull
private BaseNoteFragment getNewNoteFragment(Note newNote) {
final var mode = getPreferenceMode(getAccountId());

final var prefValueDirectEdit = getString(R.string.pref_value_mode_direct_edit);

if (mode.equals(prefValueDirectEdit)) {
return NoteDirectEditFragment.newInstanceWithNewNote(newNote);
} else {
return NoteEditFragment.newInstanceWithNewNote(newNote);
}
}

/**
* Starts the {@link NoteEditFragment} with a new note.
* Content ("share" functionality), category and favorite attribute can be preset.
Expand Down Expand Up @@ -266,12 +301,12 @@ private void launchNewNote() {
if (content == null) {
content = "";
}
// TODO handle with direct edit?
final var newNote = new Note(null, Calendar.getInstance(), NoteUtil.generateNonEmptyNoteTitle(content, this), content, categoryTitle, favorite, null);
fragment = NoteEditFragment.newInstanceWithNewNote(newNote);
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit();
fragment = getNewNoteFragment(newNote);
replaceFragment();
}


private void launchReadonlyNote() {
final var intent = getIntent();
final var content = new StringBuilder();
Expand All @@ -287,7 +322,7 @@ private void launchReadonlyNote() {
}

fragment = NoteReadonlyFragment.newInstance(content.toString());
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_view, fragment).commit();
replaceFragment();
}

@Override
Expand Down Expand Up @@ -369,7 +404,7 @@ public void changeMode(@NonNull Mode mode, boolean reloadNote) {
launchExistingNote(getAccountId(), getNoteId(), getString(R.string.pref_value_mode_preview), reloadNote);
break;
case DIRECT_EDIT:
// TODO deal with note not created yet on server
// TODO deal with note not created yet on server (e.g. when creating a new note in edit mode and switching to direct edit)
launchExistingNote(getAccountId(), getNoteId(), getString(R.string.pref_value_mode_direct_edit), reloadNote);
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import android.webkit.WebSettings
import android.widget.ScrollView
import androidx.core.view.isVisible
import com.google.android.material.snackbar.Snackbar
import com.nextcloud.android.common.ui.theme.utils.ColorRole
import com.nextcloud.android.sso.helper.SingleAccountHelper
import com.nextcloud.android.sso.model.SingleSignOnAccount
import io.reactivex.Single
Expand All @@ -26,6 +27,7 @@ import it.niedermann.owncloud.notes.databinding.FragmentNoteDirectEditBinding
import it.niedermann.owncloud.notes.persistence.ApiProvider
import it.niedermann.owncloud.notes.persistence.DirectEditingRepository
import it.niedermann.owncloud.notes.persistence.entity.Note
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI
import it.niedermann.owncloud.notes.shared.model.ApiVersion
import it.niedermann.owncloud.notes.shared.model.ISyncCallback
import it.niedermann.owncloud.notes.shared.util.ExtendedFabUtil
Expand All @@ -46,6 +48,10 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded {
)
}

val notesApi: NotesAPI by lazy {
ApiProvider.getInstance().getNotesAPI(requireContext(), account, ApiVersion.API_VERSION_1_0)
}

// for hiding / showing the fab
private var scrollStart: Int = 0

Expand Down Expand Up @@ -126,22 +132,45 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded {
}

override fun onNoteLoaded(note: Note) {
Log.d(TAG, "onNoteLoaded() called with: note = $note")
super.onNoteLoaded(note)
val newNoteParam = arguments?.getSerializable(PARAM_NEWNOTE) as Note?
if (newNoteParam != null) {
createAndLoadNote(note)
} else {
loadNoteInWebView(note)
}
}

private fun createAndLoadNote(newNote: Note) {
val noteCreateDisposable = Single
.fromCallable {
notesApi.createNote(newNote).execute().body()!!
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ createdNote ->
loadNoteInWebView(createdNote)
}, { throwable ->
note = null
handleLoadError()
Log.e(TAG, "createAndLoadNote:", throwable)
})
disposables.add(noteCreateDisposable)
}

private fun loadNoteInWebView(note: Note) {
val directEditingRepository =
DirectEditingRepository.getInstance(requireContext().applicationContext)
val urlDisposable =
directEditingRepository.getDirectEditingUrl(account, note)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ url ->
if (BuildConfig.DEBUG) {
Log.d(TAG, "onNoteLoaded: url = $url")
}
binding.noteWebview.loadUrl(url)
}, { throwable ->
handleLoadError()
Log.e(TAG, "onNoteLoaded:", throwable)
})
val urlDisposable = directEditingRepository.getDirectEditingUrl(account, note)
.observeOn(AndroidSchedulers.mainThread()).subscribe({ url ->
if (BuildConfig.DEBUG) {
Log.d(TAG, "onNoteLoaded: url = $url")
}
binding.noteWebview.loadUrl(url)
}, { throwable ->
handleLoadError()
Log.e(TAG, "onNoteLoaded:", throwable)
})
disposables.add(urlDisposable)
}

Expand Down Expand Up @@ -226,6 +255,7 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded {
override fun applyBrand(color: Int) {
val util = BrandingUtil.of(color, requireContext())
util.material.themeExtendedFAB(binding.plainEditingFab)
util.platform.colorCircularProgressBar(binding.progress, ColorRole.PRIMARY)
}

private class DirectEditingMobileInterface(val noteDirectEditFragment: NoteDirectEditFragment) {
Expand Down Expand Up @@ -257,12 +287,10 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded {

private fun changeToEditMode() {
toggleLoadingUI(true)
val notesAPI = ApiProvider.getInstance()
.getNotesAPI(requireContext(), account, ApiVersion.API_VERSION_1_0)
// TODO clean this up a bit
val updateDisposable = Single.just(note.remoteId)
.map { remoteId ->
val newNote = notesAPI.getNote(remoteId).singleOrError().blockingGet().response
val newNote = notesApi.getNote(remoteId).singleOrError().blockingGet().response
val localAccount = repo.getAccountByName(account.name)
repo.updateNoteAndSync(localAccount, note, newNote.content, newNote.title, null)
}
Expand Down Expand Up @@ -322,5 +350,14 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded {
fragment.arguments = args
return fragment
}

@JvmStatic
fun newInstanceWithNewNote(newNote: Note?): BaseNoteFragment {
val fragment = NoteDirectEditFragment()
val args = Bundle()
args.putSerializable(PARAM_NEWNOTE, newNote)
fragment.arguments = args
return fragment
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ 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() }
Expand All @@ -20,27 +18,6 @@ class DirectEditingRepository private constructor(private val 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)
Expand All @@ -54,7 +31,7 @@ class DirectEditingRepository private constructor(private val applicationContext
note: Note,
): Single<String> {
return getNotesPath(account)
.flatMap { notesPath ->
.flatMap { notesPath ->º
val filesAPI = apiProvider.getFilesAPI(applicationContext, account)
Single.fromCallable {
val call =
Expand Down

0 comments on commit 1bf7cc9

Please sign in to comment.