This pull request removes the remaining RxJava code and replaces it with
coroutine-equivalent implementations.
- Remove all duplicate methods in `MastodonApi`:
- Methods returning a RxJava `Single` have been replaced by suspending
methods returning a `NetworkResult` in order to be consistent with the
new code.
- _sync_/_async_ method variants are replaced with the _async_ version
only (suspending method), and `runBlocking{}` is used to make the async
variant synchronous.
- Create a custom coroutine-based implementation of `Single` for usage
in Java code where launching a coroutine is not possible. This class can
be deleted after remaining Java code has been converted to Kotlin.
- `NotificationsFragment.java` can subscribe to `EventHub` events by
calling the new lifecycle-aware `EventHub.subscribe()` method. This
allows using the `SharedFlow` as single source of truth for all events.
- Rx Autodispose is replaced by `lifecycleScope.launch()` which will
automatically cancel the coroutine when the Fragment view/Activity is
destroyed.
- Background work is launched in the existing injectable
`externalScope`, since using `GlobalScope` is discouraged.
`externalScope` has been changed to be a `@Singleton` and to use the
main dispatcher by default.
- Transform `ShareShortcutHelper` to an injectable utility class so it
can use the application `Context` and `externalScope` as provided
dependencies to launch a background coroutine.
- Implement a custom Glide extension method
`RequestBuilder.submitAsync()` to do the same thing as
`RequestBuilder.submit().get()` in a non-blocking way. This way there is
no need to switch to a background dispatcher and block a background
thread, and cancellation is supported out-of-the-box.
- An utility method `Fragment.updateRelativeTimePeriodically()` has been
added to remove duplicate logic in `TimelineFragment` and
`NotificationsFragment`, and the logic is now implemented using a simple
coroutine instead of `Observable.interval()`. Note that the periodic
update now happens between onStart and onStop instead of between
onResume and onPause, since the Fragment is not interactive but is still
visible in the started state.
- Rewrite `BottomSheetActivityTest` using coroutines tests.
- Remove all RxJava library dependencies.
There are some new rules, I think they mostly make sense, except for the
max line length which I had to disable because we are over it in a lot
of places.
---------
Co-authored-by: Goooler <wangzongler@gmail.com>
Instead of repeating the same if/else check on the error type when setting up the background message, move this in to BackgroundMessageView.
Provide different `setup()` variants, including one that just takes a throwable and a handler, and figures out the correct drawables and error message.
Update and simplify call sites.
* Add "Refresh" accessibility menu to TimelineFragment
Per https://developer.android.com/reference/androidx/swiperefreshlayout/widget/SwipeRefreshLayout
the layout does not provide accessibility events, and a menu item should be
provided as an alternative method for refreshing the content.
In `TimelineFragment`:
- Implement the `MenuProvider` interface so it can populate the action bar
menu in activities that host the fragment
- Create a "Refresh" menu item, and refresh the state when it is selected
`MainActivity` has to change how the menu is created, so that fragments
can add items to it.
In `MainActivity`:
- Call `setSupportActionBar` so `mainToolbar` participates in menus
- Implement the `MenuProvider` interface, and move menu creation there
- Set the title via supportActionBar
* Never show the refresh item as a menubar action
Per guidelines in https://developer.android.com/develop/ui/views/touch-and-input/swipe/add-swipe-interface#AddRefreshAction
* Add "Refresh" menu item for AccountMediaFragment
Also, fix the colour of the refresh progress indicator
* Implement "Refresh" for AnnouncementsActivity
* Add "Refresh" menu for ConversationsFragment
* Keep the tabs adapter over the life of the viewpager
Make `tabs` `var` instead of `val` in `MainPagerAdapter` so it can be updated
when tabs change.
Then detach the `tabLayoutMediator`, update the tabs, and call
`notifyItemRangeChanged` in `setupTabs()`.
This fixes a bug (not sure if it's this code, or in ViewPager2) where
assigning a new adapter to the view pager seemed to result in a leak of one
or more fragments. This wasn't user-visible, but it's a leak, and it becomes
user-visible when fragments want to display menus.
This also fixes two other bugs:
1. Be on the left-most tab. Scroll down a bit. Then modify the tabs at
"Account preferences > tabs", but keep the left-most tab as-is.
Then go back to MainActivity. Your reading position in the left-most
tab has been jumped to the top.
2. Be on any non-left-most tab. Then modify the tab list by reordering tabs
(adding/removing tabs is also OK).
Then go back to MainActivity. Your tab selection has been overridden,
and the left-most tab has been selected.
Because the fragments are not destroyed unnecessarily your reading position
is retained. And it remembers the tab you had selected, and as long as that
tab is still present you will be returned to it, even if it's changed
position in the list.
Fixes https://github.com/tuskyapp/Tusky/issues/3251
* Add "Refresh" menu for ScheduledStatusActivity
* Lint
* Add "Refresh" menu for SearchFragment / SearchActivity
* Explicitly set the searchview width
Using "collapseActionView" requires the user to press "Back" twice to exit
the activity, which is not acceptable.
* Move toolbar handling in to ViewThreadActivity
Previous code had the toolbar in the fragment's layout. Refactor to make
consistent with other activities, and move the toolbar in to the activity
layout.
Implement MenuProvider in ViewThreadFragment to adjust the menu in the
activity.
* Add "Refresh" menu to ViewThreadFragment
* Implement "Refresh" for ViewEditsFragment
* Lint
* Add "Refresh" menu to ReportStatusesFragment
* Add "Refresh" menu to NotificationsFragment
* Rename menu resource files
Be consistent with the layout resource files, which have an activity/fragment
prefix, then the lower_snake_case name of the activity or fragment it's for.
* Only enable refresh menu if swiptorefresh is enabled
Some timelines don't have swipetorefresh enabled (e.g., those shown on
AccountActivity). In those cases don't add the refresh menu, rely on the
hosting activity to provide it.
Update AccountActivity to provide the refresh menu item.
* Fix saving changes to statuses when editing
With the previous code backing out of a status editing operation where changes
had been made (whether it was editing an existing status, a scheduled status,
or a draft) would prompt the user to save the changes as a new draft.
See https://github.com/tuskyapp/Tusky/issues/2704 and
https://github.com/tuskyapp/Tusky/issues/2705 for more detail.
The fix:
- Create an enum to represent the four different kinds of edits that can
happen
- Editing a new status (i.e., composing it for the first time)
- Editing a posted status
- Editing a draft
- Editing a scheduled status
- Store this in ComposeOptions, and set it appropriately everywhere
ComposeOptions is created.
- Check the edit kind when backing out of ComposeActivity, and use this to
show one of three different dialogs as appropriate so the user can:
- Save as new draft or discard changes
- Continue editing or discard changes
- Update existing draft or discard changes
Also fix ComposeViewModel.didChange(), which erroneously reported false if the
old text started with the new text (e.g., if the old text was "hello, world"
and it was edited to "hello", didChange() would not consider that to be a
change).
Fixes https://github.com/tuskyapp/Tusky/issues/2704,
https://github.com/tuskyapp/Tusky/issues/2705
* Use orEmpty extension function
* Leave the "edit scheduled status" button enabled after clicking
If the user submits an edit to the scheduled status then this one will be deleted, the paging source will notice, the adapter will be notified in the normal way, and this binding will be reused.
Or the user backs out of the edit, and this adapter entry is still valid and should remain clickable.
Fixes https://github.com/tuskyapp/Tusky/issues/2705
* Remove unnecessary parameter.
* Remove unnecessary import
* update gradle, kotlin and other dependencies
* fix new warnings
* remove unused import
* update Proguard rules
* add explicit dependency on Gson to get the newest version
* remove debug flag from proguard rules again
* fix typo
* cleanup warnings, reorganize some code
* move ComposeAutoCompleteAdapter to compose package
* composeOptions doesn't need to be a class member
* add DraftsActivity and DraftsViewModel
* drafts
* remove unnecessary Unit in ComposeViewModel
* add schema/25.json
* fix db migration
* drafts
* cleanup code
* fix compose activity rotation bug
* fix media descriptions getting lost when restoring a draft
* improve deleting drafts
* fix ComposeActivityTest
* improve draft layout for almost empty drafts
* reformat code
* show toast when opening reply to deleted toot
* improve item_draft layout
* Delete scheduled post after editing
This ensures that editing a scheduled toot will not duplicate it.
Fixes#1924
* Fix scheduled toot editing
* Convert isEditingScheduledToot to property
* Replace MediatorLiveData with combineLiveData
* Remove dead code
* Fix observer emission
* Fix "Finish media upload" appearing for split second