diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java deleted file mode 100644 index 88a3b6c03..000000000 --- a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Nextcloud Notes - Android Client - * - * SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package it.niedermann.owncloud.notes.branding; - -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuInflater; - -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -public abstract class BrandedFragment extends Fragment implements Branded { - - @ColorInt - protected int colorAccent; - @ColorInt - protected int colorPrimary; - - @Override - public void onStart() { - super.onStart(); - - final var context = requireContext(); - final var typedValue = new TypedValue(); - context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorAccent, typedValue, true); - colorAccent = typedValue.data; - context.getTheme().resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue, true); - colorPrimary = typedValue.data; - - @ColorInt final int color = BrandingUtil.readBrandMainColor(context); - applyBrand(color); - } - - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - final var utils = BrandingUtil.of(colorAccent, requireContext()); - - for (int i = 0; i < menu.size(); i++) { - if (menu.getItem(i).getIcon() != null) { - utils.platform.colorToolbarMenuIcon(requireContext(), menu.getItem(i)); - } - } - } -} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.kt b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.kt new file mode 100644 index 000000000..09333c44c --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/branding/BrandedFragment.kt @@ -0,0 +1,117 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.branding + +import android.os.Bundle +import android.util.TypedValue +import android.view.Menu +import android.view.MenuInflater +import androidx.annotation.ColorInt +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.forEach +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import com.nextcloud.android.common.ui.util.extensions.adjustUIForAPILevel35 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +/** + * An abstract base [Fragment] implementation that provides common branding support for UI + * components. + * + * This class reads and applies brand-specific colors (`colorPrimary`, `colorAccent`, etc.) when the + * fragment starts, and adjusts UI elements such as toolbar menu icons accordingly. + * + * Subclasses can extend this to inherit branding behavior while implementing their specific logic. + * + * @see BrandingUtil for brand color resolution and application. + * @see Branded for the interface definition related to branding behavior. + */ +abstract class BrandedFragment : Fragment(), Branded { + @JvmField + @ColorInt + protected var colorAccent: Int = 0 + + @JvmField + @ColorInt + protected var colorPrimary: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + if (activity is AppCompatActivity) { + val appCompatActivity = activity as AppCompatActivity + appCompatActivity.adjustUIForAPILevel35() + } + super.onCreate(savedInstanceState) + } + + override fun onStart() { + super.onStart() + + val context = requireContext() + val typedValue = TypedValue() + + context.theme.resolveAttribute( + com.google.android.material.R.attr.colorAccent, + typedValue, + true + ) + colorAccent = typedValue.data + + context.theme.resolveAttribute( + com.google.android.material.R.attr.colorPrimary, + typedValue, + true + ) + colorPrimary = typedValue.data + + @ColorInt + val color = BrandingUtil.readBrandMainColor(context) + applyBrand(color) + } + + @Suppress("DEPRECATION") + @Deprecated("Deprecated in Java") + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + val utils = BrandingUtil.of(colorAccent, requireContext()) + + menu.forEach { menu -> + menu.icon?.let { icon -> + utils.platform.colorToolbarMenuIcon(requireContext(), menu) + } + } + } + + /** + * Launches the given [block] of code in the [Dispatchers.IO] context using the [lifecycleScope]. + * + * This is useful for running long-running or blocking operations (e.g., file or network I/O) + * that should not block the main thread. The coroutine will be automatically canceled when + * the lifecycle is destroyed. + * + * @param block The code block to be executed on the IO dispatcher. + */ + fun lifecycleScopeIOJob(block: () -> Unit) { + lifecycleScope.launch(Dispatchers.IO) { + block() + } + } + + /** + * Executes the given [block] on the main (UI) thread. + * + * This is typically used to perform UI-related tasks such as updating views from a background + * thread. Requires [activity] to be non-null; otherwise, the block will not be executed. + * + * @param block The code block to be executed on the main thread. + */ + fun onMainThread(block: () -> Unit) { + activity?.runOnUiThread { + block() + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt index ca553d2d9..f9636b929 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteDirectEditFragment.kt @@ -27,6 +27,7 @@ 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 com.owncloud.android.lib.common.utils.Log_OC import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -44,12 +45,13 @@ import it.niedermann.owncloud.notes.shared.model.ApiVersion import it.niedermann.owncloud.notes.shared.model.ISyncCallback import it.niedermann.owncloud.notes.shared.util.ExtendedFabUtil import it.niedermann.owncloud.notes.shared.util.rx.DisposableSet +import okio.IOException import java.util.concurrent.TimeUnit class NoteDirectEditFragment : BaseNoteFragment(), Branded { private var _binding: FragmentNoteDirectEditBinding? = null - private val binding: FragmentNoteDirectEditBinding - get() = _binding!! + private val binding: FragmentNoteDirectEditBinding? + get() = _binding private val disposables: DisposableSet = DisposableSet() private var switchToEditPending = false @@ -79,41 +81,44 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, - ): View { + ): View? { Log.d(TAG, "onCreateView() called") _binding = FragmentNoteDirectEditBinding.inflate(inflater, container, false) setupFab() prepareWebView() - return binding.root + return binding?.root } @SuppressLint("ClickableViewAccessibility") // touch listener only for UI purposes, no need to handle click private fun setupFab() { - binding.plainEditingFab.isExtended = false - ExtendedFabUtil.toggleExtendedOnLongClick(binding.plainEditingFab) - // manually detect scroll as we can't get it from the webview (maybe with custom JS?) - binding.noteWebview.setOnTouchListener { _, event -> - when (event.action) { - MotionEvent.ACTION_DOWN -> { - scrollStart = event.y.toInt() - } - MotionEvent.ACTION_UP -> { - val scrollEnd = event.y.toInt() - ExtendedFabUtil.toggleVisibilityOnScroll( - binding.plainEditingFab, - scrollStart, - scrollEnd, - ) + binding?.run { + plainEditingFab.isExtended = false + ExtendedFabUtil.toggleExtendedOnLongClick(plainEditingFab) + + // manually detect scroll as we can't get it from the webview (maybe with custom JS?) + noteWebview.setOnTouchListener { _, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + scrollStart = event.y.toInt() + } + MotionEvent.ACTION_UP -> { + val scrollEnd = event.y.toInt() + ExtendedFabUtil.toggleVisibilityOnScroll( + plainEditingFab, + scrollStart, + scrollEnd, + ) + } } + return@setOnTouchListener false } - return@setOnTouchListener false + plainEditingFab.setOnClickListener { switchToPlainEdit() } } - binding.plainEditingFab.setOnClickListener { switchToPlainEdit() } } private fun switchToPlainEdit() { switchToEditPending = true - binding.noteWebview.evaluateJavascript(JS_CLOSE) { result -> + binding?.noteWebview?.evaluateJavascript(JS_CLOSE) { result -> val resultWithoutQuotes = result.replace("\"", "") if (resultWithoutQuotes != JS_RESULT_OK) { Log.w(TAG, "Closing via JS failed: $resultWithoutQuotes") @@ -126,7 +131,7 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { override fun onDestroyView() { super.onDestroyView() disposables.dispose() - binding.noteWebview.destroy() + binding?.noteWebview?.destroy() _binding = null } @@ -135,7 +140,7 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { val timeoutDisposable = Single.just(Unit) .delay(LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS) .map { - if (!binding.noteWebview.isVisible) { + if (binding?.noteWebview?.isVisible == false) { Log.w(TAG, "Editor not loaded after $LOAD_TIMEOUT_SECONDS seconds") handleLoadError() } @@ -158,11 +163,19 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { Log.d(TAG, "createAndLoadNote() called") val noteCreateDisposable = Single .fromCallable { - notesApi.createNote(newNote).execute().body()!! + try { + val response = notesApi.createNote(newNote).execute() + response.body() + } catch (e: IOException) { + Log_OC.w(TAG, "Cant able to create a note: $e") + null + } } - .map { createdNote -> - repo.updateRemoteId(newNote.id, createdNote.remoteId) - repo.getNoteById(newNote.id) + .flatMap { createdNote -> + createdNote?.let { + repo.updateRemoteId(newNote.id, it.remoteId) + Single.fromCallable { repo.getNoteById(newNote.id) } + } } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -178,80 +191,89 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { private fun loadNoteInWebView(note: Note) { Log.d(TAG, "loadNoteInWebView() called") - val directEditingRepository = - DirectEditingRepository.getInstance(requireContext().applicationContext) - val urlDisposable = directEditingRepository.getDirectEditingUrl(account, note) - .observeOn(AndroidSchedulers.mainThread()).subscribe({ url -> - if (BuildConfig.DEBUG) { - Log.d(TAG, "loadNoteInWebView: url = $url") - } - binding.noteWebview.loadUrl(url) - }, { throwable -> - handleLoadError() - Log.e(TAG, "loadNoteInWebView:", throwable) - }) - disposables.add(urlDisposable) + + context?.let { context -> + val repository = DirectEditingRepository.getInstance(context.applicationContext) + val urlDisposable = repository.getDirectEditingUrl(account, note) + .observeOn(AndroidSchedulers.mainThread()).subscribe({ url -> + url?.let { + if (BuildConfig.DEBUG) { + Log.d(TAG, "loadNoteInWebView: url = $url") + } + binding?.noteWebview?.loadUrl(url) + } + }, { throwable -> + handleLoadError() + Log.e(TAG, "loadNoteInWebView:", throwable) + }) + disposables.add(urlDisposable) + } } private fun handleLoadError() { - val snackbar = BrandedSnackbar.make( - binding.plainEditingFab, - getString(R.string.direct_editing_error), - Snackbar.LENGTH_INDEFINITE, - ) - if (note != null) { - snackbar.setAction(R.string.switch_to_plain_editing) { - changeToEditMode() - } - } else { - snackbar.setAction(R.string.action_back) { - close() + binding?.run { + val snackbar = BrandedSnackbar.make( + plainEditingFab, + getString(R.string.direct_editing_error), + Snackbar.LENGTH_INDEFINITE, + ) + + if (note != null) { + snackbar.setAction(R.string.switch_to_plain_editing) { + changeToEditMode() + } + } else { + snackbar.setAction(R.string.action_back) { + close() + } } + + snackbar.show() } - snackbar.show() } override fun shouldShowToolbar(): Boolean = false @SuppressLint("SetJavaScriptEnabled") private fun prepareWebView() { - val webSettings = binding.noteWebview.settings - // enable zoom - webSettings.setSupportZoom(true) - webSettings.builtInZoomControls = true - webSettings.displayZoomControls = false - - // Non-responsive webs are zoomed out when loaded - webSettings.useWideViewPort = true - webSettings.loadWithOverviewMode = true - - // user agent - val userAgent = - getString(R.string.user_agent, getString(R.string.app_name), BuildConfig.VERSION_NAME) - webSettings.userAgentString = userAgent - - // no private data storing - webSettings.savePassword = false - webSettings.saveFormData = false - - // disable local file access - webSettings.allowFileAccess = false - - // enable javascript - webSettings.javaScriptEnabled = true - webSettings.domStorageEnabled = true + binding?.noteWebview?.settings?.run { + // enable zoom + setSupportZoom(true) + builtInZoomControls = true + displayZoomControls = false + + // Non-responsive webs are zoomed out when loaded + useWideViewPort = true + loadWithOverviewMode = true + + // user agent + val userAgent = + getString(R.string.user_agent, getString(R.string.app_name), BuildConfig.VERSION_NAME) + userAgentString = userAgent + + // no private data storing + savePassword = false + saveFormData = false + + // disable local file access + allowFileAccess = false + + // enable javascript + javaScriptEnabled = true + domStorageEnabled = true + } if (BuildConfig.DEBUG) { // caching disabled in debug mode - binding.noteWebview.settings.cacheMode = WebSettings.LOAD_NO_CACHE + binding?.noteWebview?.settings?.cacheMode = WebSettings.LOAD_NO_CACHE } - binding.noteWebview.addJavascriptInterface( + binding?.noteWebview?.addJavascriptInterface( DirectEditingMobileInterface(this), JS_INTERFACE_NAME, ) - binding.noteWebview.webViewClient = object : WebViewClient() { + binding?.noteWebview?.webViewClient = object : WebViewClient() { override fun onReceivedError( view: WebView?, request: WebResourceRequest?, @@ -299,8 +321,11 @@ 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) + + binding?.run { + util.material.themeExtendedFAB(plainEditingFab) + util.platform.colorCircularProgressBar(progress, ColorRole.PRIMARY) + } } private class DirectEditingMobileInterface(val noteDirectEditFragment: NoteDirectEditFragment) { @@ -331,6 +356,11 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { } private fun changeToEditMode() { + if (note == null || note.remoteId == null) { + Log.d(TAG, "note is null, cant edit") + return + } + toggleLoadingUI(true) val updateDisposable = Single.just(note.remoteId) .map { remoteId -> @@ -360,9 +390,11 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { private fun toggleLoadingUI(loading: Boolean) { activity?.runOnUiThread { - binding.progress.isVisible = loading - binding.noteWebview.isVisible = !loading - binding.plainEditingFab.isVisible = !loading + binding?.run { + progress.isVisible = loading + noteWebview.isVisible = !loading + plainEditingFab.isVisible = !loading + } } } @@ -387,21 +419,25 @@ class NoteDirectEditFragment : BaseNoteFragment(), Branded { @JvmStatic fun newInstance(accountId: Long, noteId: Long): BaseNoteFragment { - val fragment = NoteDirectEditFragment() - val args = Bundle() - args.putLong(PARAM_NOTE_ID, noteId) - args.putLong(PARAM_ACCOUNT_ID, accountId) - fragment.arguments = args - return fragment + val bundle = Bundle().apply { + putLong(PARAM_NOTE_ID, noteId) + putLong(PARAM_ACCOUNT_ID, accountId) + } + + return NoteDirectEditFragment().apply { + arguments = bundle + } } @JvmStatic fun newInstanceWithNewNote(newNote: Note?): BaseNoteFragment { - val fragment = NoteDirectEditFragment() - val args = Bundle() - args.putSerializable(PARAM_NEWNOTE, newNote) - fragment.arguments = args - return fragment + val bundle = Bundle().apply { + putSerializable(PARAM_NEWNOTE, newNote) + } + + return NoteDirectEditFragment().apply { + arguments = bundle + } } } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java index 168fcb2d0..bc68e54c6 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NoteEditFragment.java @@ -40,6 +40,8 @@ import it.niedermann.owncloud.notes.persistence.entity.Note; import it.niedermann.owncloud.notes.shared.model.ISyncCallback; import it.niedermann.owncloud.notes.shared.util.DisplayUtils; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; public class NoteEditFragment extends SearchableBaseNoteFragment { @@ -180,22 +182,35 @@ public void onResume() { @Override protected void onNoteLoaded(Note note) { super.onNoteLoaded(note); + if (binding == null) { + return; + } + if (TextUtils.isEmpty(note.getContent())) { openSoftKeyboard(); } - binding.editContent.setMarkdownString(note.getContent()); - binding.editContent.setEnabled(true); + lifecycleScopeIOJob(() -> { + // load potential big note on IO Dispatchers + final String content = note.getContent(); + final var sp = PreferenceManager.getDefaultSharedPreferences(requireContext().getApplicationContext()); - final var sp = PreferenceManager.getDefaultSharedPreferences(requireContext().getApplicationContext()); - binding.editContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(requireContext(), sp)); - if (sp.getBoolean(getString(R.string.pref_key_font), false)) { - binding.editContent.setTypeface(Typeface.MONOSPACE); - } + onMainThread(() -> { + binding.editContent.setMarkdownString(content); + binding.editContent.setEnabled(true); + binding.editContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, getFontSizeFromPreferences(requireContext(), sp)); - if (lastSelection > 0 && binding.editContent.length() >= lastSelection) { - binding.editContent.setSelection(lastSelection); - } + if (sp.getBoolean(getString(R.string.pref_key_font), false)) { + binding.editContent.setTypeface(Typeface.MONOSPACE); + } + + if (lastSelection > 0 && binding.editContent.length() >= lastSelection) { + binding.editContent.setSelection(lastSelection); + } + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }); } private void openSoftKeyboard() { diff --git a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java index 368fb5d9d..bb264fc3c 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/edit/NotePreviewFragment.java @@ -34,12 +34,16 @@ import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; import com.nextcloud.android.sso.helper.SingleAccountHelper; +import com.owncloud.android.lib.common.utils.Log_OC; import it.niedermann.owncloud.notes.R; import it.niedermann.owncloud.notes.branding.BrandingUtil; import it.niedermann.owncloud.notes.databinding.FragmentNotePreviewBinding; import it.niedermann.owncloud.notes.persistence.entity.Note; +import it.niedermann.owncloud.notes.shared.model.ISyncCallback; import it.niedermann.owncloud.notes.shared.util.SSOUtil; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; public class NotePreviewFragment extends SearchableBaseNoteFragment implements OnRefreshListener { @@ -135,11 +139,26 @@ protected void onNoteLoaded(Note note) { super.onNoteLoaded(note); noteLoaded = true; registerInternalNoteLinkHandler(); - changedText = note.getContent(); - binding.singleNoteContent.setMarkdownString(note.getContent(), setScrollY); - binding.singleNoteContent.getMarkdownString().observe(requireActivity(), (newContent) -> { - changedText = newContent.toString(); - saveNote(null); + + lifecycleScopeIOJob(() -> { + final String content = note.getContent(); + changedText = content; + + onMainThread(() -> { + binding.singleNoteContent.setMarkdownString(content, setScrollY); + + final var activity = getActivity(); + if (activity == null) { + return Unit.INSTANCE; + } + + binding.singleNoteContent.getMarkdownString().observe(activity, (newContent) -> { + changedText = newContent.toString(); + saveNote(null); + }); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; }); } @@ -176,21 +195,27 @@ protected String getContent() { public void onRefresh() { if (noteLoaded && repo.isSyncPossible() && SSOUtil.isConfigured(getContext())) { binding.swiperefreshlayout.setRefreshing(true); - executor.submit(() -> { + lifecycleScopeIOJob(() -> { try { final var account = repo.getAccountByName(SingleAccountHelper.getCurrentSingleSignOnAccount(requireContext()).name); - repo.addCallbackPull(account, () -> executor.submit(() -> { + + repo.addCallbackPull(account, () -> { note = repo.getNoteById(note.getId()); - changedText = note.getContent(); - requireActivity().runOnUiThread(() -> { - binding.singleNoteContent.setMarkdownString(note.getContent()); + final String content = note.getContent(); + changedText = content; + + onMainThread(() -> { + binding.singleNoteContent.setMarkdownString(content); binding.swiperefreshlayout.setRefreshing(false); + return Unit.INSTANCE; }); - })); + }); + repo.scheduleSync(account, false); - } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { - e.printStackTrace(); + } catch (Exception e) { + Log_OC.e(TAG, "onRefresh exception: " + e); } + return Unit.INSTANCE; }); } else { binding.swiperefreshlayout.setRefreshing(false); diff --git a/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt b/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt index 09ab1d413..8e8ece16c 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt +++ b/app/src/main/java/it/niedermann/owncloud/notes/persistence/DirectEditingRepository.kt @@ -24,19 +24,20 @@ class DirectEditingRepository private constructor(private val applicationContext ) } - private fun getNotesPath(account: SingleSignOnAccount): Single { + private fun getNotesPath(account: SingleSignOnAccount): Single { return Single.fromCallable { val call = notesRepository.getServerSettings(account, ApiVersion.API_VERSION_1_0) val response = call.execute() - response.body()?.notesPath ?: throw RuntimeException("No notes path available") + response.body()?.notesPath }.subscribeOn(Schedulers.io()) } fun getDirectEditingUrl( account: SingleSignOnAccount, note: Note, - ): Single { - return getNotesPath(account) + ): Single { + val notesPath = getNotesPath(account) + return notesPath .flatMap { notesPath -> val filesAPI = apiProvider.getFilesAPI(applicationContext, account) Single.fromCallable { @@ -50,7 +51,6 @@ class DirectEditingRepository private constructor(private val applicationContext ) val response = call.execute() response.body()?.ocs?.data?.url - ?: throw RuntimeException("No url available") }.subscribeOn(Schedulers.io()) } } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java index 8508d5fe8..939973a37 100644 --- a/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/notelist/NoteListWidgetFactory.java @@ -64,6 +64,11 @@ public void onDataSetChanged() { dbNotes.clear(); try { data = repo.getNoteListWidgetData(appWidgetId); + if (data == null) { + Log.w(TAG, "Widget data is null"); + return; + } + Log.v(TAG, "--- data - " + data); switch (data.getMode()) { case MODE_DISPLAY_ALL -> @@ -78,7 +83,7 @@ public void onDataSetChanged() { } } } - } catch (IllegalArgumentException e) { + } catch (Exception e) { Log.w(TAG, "Error caught at onDataSetChanged: " + e); } }