Commit Graph

4126 Commits

Author SHA1 Message Date
Nik Clayton d87a6718cc change: Use "…" instead of "..." in Korean localisation 2024-04-30 16:23:34 +02:00
Nik Clayton 86d79276c6 change: Correct some package references in KDoc 2024-04-30 16:23:34 +02:00
Nik Clayton 1f50a9cbe7 change: Remove unnecessary types 2024-04-30 16:23:34 +02:00
Nik Clayton 4ddd23dda6 change: Correct constructor parameter visibility 2024-04-30 16:23:34 +02:00
Nik Clayton c8532c9fb4 change: Remove dead code 2024-04-30 16:23:34 +02:00
Nik Clayton 6fd993acf6 change: Use supportFragmentManager.commit 2024-04-30 16:23:34 +02:00
Nik Clayton 286a152fde change: Move MediaUploadApi to the correct package 2024-04-30 16:23:34 +02:00
Nik Clayton 2d0cf6c17e change: Mark some properties private 2024-04-30 16:23:34 +02:00
Nik Clayton bf474a5eef
fix(deps): update dependency androidx.core:core-ktx to v1.13.0 (#673)
Use `GestureDetector` as this release deprecates
`GestureDetectorCompat`.
2024-04-30 16:14:11 +02:00
Nik Clayton 2236f53838
fix: Show edit history for status' with polls (#672)
Edited polls only include the list of options with titles; no other
metadata (poll ID, single/multiple choice, vote counts, etc). Since the
data shape didn't match Moshi wasn't decoding the data.

Provide dedicated data classes to model the response, and add a fourth
poll display option to represent viewing an edit history snapshot.
2024-04-30 14:38:36 +02:00
Nik Clayton c7783b65f9
refactor: Use AppCompatResources.getDrawable() (#671)
Replace `ContextCompat.getDrawable()`, as the app-compat version has
platform fixes and backports.

appcompat-lint warns about bare `context.getDrawable()`, but does not
cover `ContextCompat` (https://issuetracker.google.com/issues/337905331)
2024-04-30 13:30:19 +02:00
Nik Clayton c6d6f0f810
chore: Prepare release 2.5.1 (versionCode 15) (#670) 2024-04-29 18:52:34 +02:00
Nik Clayton 22729df1b7
fix: Only enable accessible actions on notifications attached to a status
Fixes #669
2024-04-29 18:43:39 +02:00
Nik Clayton fc5061f777
chore: Prepare release 2.5.0 (versionCode 14) (#668) 2024-04-29 12:44:59 +02:00
Miles Krell 6fed74099a fix(l10n): Update Spanish translations
Currently translated at 100.0% (609 of 609 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/es/
2024-04-29 08:58:44 +02:00
Nik Clayton e65bc13184
fix: Mark tab drag icon as unimportant for accessiblity (#663)
Talkback was speaking it as "Unlabeled". It's not necessary for
Talkback, as the tab name describes it in the list.
2024-04-28 23:53:38 +02:00
Nik Clayton f771dd7026
fix: Enable talkback actions in notifications and conversations (#661)
`ListStatusAccessiblityDelegate` was ignoring notifications because it
was checking the status against the concrete `StatusViewData` and not
the interface `IStatusViewData`.

Fix that and now notifications have accessibility actions.

`ConversationsFragment` didn't set the accessibility delegate, so no
actions appeared. Fix that so they do.
2024-04-28 23:46:11 +02:00
Nik Clayton 362cdfeb27
fix: Prevent crash when Pachli is a share target (#659)
Crash was occuring because the instance info hadn't been fetched, trying
to take the last item of an empty list.

To fix:

- Expose the instance info as a state flow, with a default. New instance
info is fetched whenever the active account changes.

- Do the same for the emojis supported by the server.

- Update call sites as appropriate.

- Mark `InstanceInfoRepository` as `@Singleton` so it isn't repeatedly
created causing fresh content fetches.

The tests needed updating to get this to work.

- Extract the network fake modules in to a network-test module so
multiple other modules can use them.

- Rewrite `InstanceInfoRepositoryTest` to use Hilt and use Turbine to
test the new flows.

Checking this showed cosmetic bugs in the About layout when instance
info is missing, clean those up.
2024-04-28 18:19:13 +02:00
Miles Krell 1f4930ff79 fix(l10n): Update Spanish translations
Currently translated at 100.0% (606 of 606 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/es/
2024-04-27 02:08:54 +02:00
Nik Clayton 78cda01ec8
change: Adjust SwipeRefreshLayout offset in AccountActivity (#652)
The change in 5959d23d resulted in a stuck margin when scrolling up in
the account view.

Fix the `SwipeRefreshLayout` offset in a different way that doesn't have
that problem; move the spinner's start position to the top of the screen
(instead of the negative offset it normally has) and keep the same
relative offset from the end position.

Have it scale in on the drag to complete the effect.
2024-04-27 00:15:21 +02:00
Nik Clayton 93e6b38d43
feat: Update activity transitions, prepare for predictive-back (#650)
Previous code used a single animation type (slide) when transitioning,
the transition was quite slow, and didn't behave appropriately if the
device was set to a RTL writing system.

In addition, handling the back affordance didn't work well with the new
"Predictive Back" feature in Android 14

(https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture).

Fix this.

## Transitions

To update the transitions the `startActivityWithSlideInAnimation()`
implementation (and associated `finishWithoutSlideOutAnimation()`) have
been replaced.

There are three transitions; `default`, `slide`, and `explode`,
represented as an enum passed to the activity intent.

The `default` transition is the activity transition from Android 14,
from the Android open source project

(https://cs.android.com/android/platform/superproject/+/android-14.0.0_r18:frameworks/base/core/res/res/anim/;bpv=1).
This is used for most transitions.

The `slide` transition is the pre-existing slide transition, with a
shorter transition time so it feels more responsive, and an RTL
implementation. This is used when there is a strong spatial component to
the navigation, for example, when going from:

- a status to its thread
- a preference menu item to its subscreen
- a filter in a list to the "edit filter" screen
- viewing your profile to editing your profile

The `explode` transition is used when the state of the app changes
significantly, such as when switching accounts.

Activities are now started with `startActivityWithTransition()` which
sets the intent and prepares the transition. `BaseActivity` checks the
intent for the transition type and makes further changes to the
transition as necessary.

## Predictive back

"Predictive back" needs to know what the back button would do before the
user interacts with it with an `onBackPressedCallback` that is either
enabled or disabled. This required refactoring some code (particularly
in `ComposeActivity`) to gather data ahead of time and enable/disable
the callback appropriately.

## Fixed bugs

- Back button wasn't stepping back through the tabs in AccountActivity
- Modifying a filter and pressing back without saving wasn't prompting
the user to save the changes
- Writing a content warning and then hiding it would still count the
text of the content warning toward's the post's length

## Other cleanups

- Use `ViewCompat.setTransitionName()` instead of setting the
`transitionName` property
- Delete the unused `fade_in` and `fade_out` animations.
- Use androidx-activity 1.9.0 to get the latest predictive back support
library code
- Show validation errors when creating / editing filters
2024-04-26 23:18:30 +02:00
Nik Clayton 12eccb63ae
refactor: Prefer System.currentTimeMillis() to Date().getTime() (#645)
Avoids unnecessary object creation.

Add a lint rule to catch this.
2024-04-24 12:07:05 +02:00
Nik Clayton 3e1d94ded2
feat: Use Let's Encrypt certificates on API 23 devices (#640)
Android 7 devices no longer trust certificates issued by Let's Encrypt
(see https://letsencrypt.org/2020/11/06/own-two-feet and
https://letsencrypt.org/2023/07/10/cross-sign-expiration.html for
details).

To work around that provide the Let's Encrypt root certs as resources.

On API 24+ devices add those via network_security_config.xml.

On API 23 devices they need to be installed manually for OkHttp SSL
connections, and checked when there is an SSL error in
LoginWebViewActivity.

The root certificates were downloaded from
https://letsencrypt.org/certificates/:

- https://letsencrypt.org/certs/isrgrootx1.der (self-signed)
- https://letsencrypt.org/certs/isrg-root-x1-cross-signed.der
(cross-signed)
- https://letsencrypt.org/certs/isrg-root-x2.der (self-signed)
- https://letsencrypt.org/certs/isrg-root-x2-cross-signed.der
(cross-signed)

Fixes #638
2024-04-24 10:32:50 +02:00
Miles Krell 4fdb997d7e fix(l10n): Update Spanish translations
Currently translated at 100.0% (606 of 606 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/es/
2024-04-23 22:25:56 +02:00
Nik Clayton 5959d23d89
change: Move SwipeRefreshLayout margin in AccountActivity (#642)
AccountActivity is full screen, so the swipe spinner was appearing very
close to the top system bar. Adjust the spinner's top margin to avoid
the system bar, and have it scale in/out so it doesn't appear to slide
out from an invisible barrier.
2024-04-23 15:58:32 +02:00
Nik Clayton b3bce36ccc
fix: Improve transitions in/out of playing video (#636)
Previous code didn't trigger the transition from `ViewMediaActivity`
when playing a video until the video had loaded. If the connection was
slow or had other issues this resulted in the video "sticking" in the
timeline until it loaded.

Change this, and trigger the transition immediately.

Fixes #598

While looking at this:

- Save the play/pause state of the video during a swipe, pause the
video, and restore the state when the swipe is cancelled.
- Transition the entire video view, to improve the animated transition
back to the activity.
- Remove the custom progress spinner, use the one provided by the
player.
- Display the player controller via the layout XML instead of code
2024-04-23 11:35:11 +02:00
Nik Clayton aa930f3a6f
feat: Fetch more trending posts, links, and hashtags (#634)
The previous code didn't set a limit for the number of posts, links, and
hashtags to fetch on the trending pages, so used the conservative
defaults.

Increase these to the API maximums to show the user more information.
2024-04-22 21:34:22 +02:00
Nik Clayton b0d63e4243
fix: Update PageCache to handle non-chronological ordering (#633)
The PageCache implementation wasn't properly dealing with timelines that
could return statuses in non-chronological order.

For example, if you bookmark a recent status, then go back in the
timeline and bookmark an older status; the bookmarks timeline is ordered
by the time of the bookmark event, not the creation time of the status
that was bookmarked.

If a sufficiently old status was bookmarked so it straddled a page
boundary you could have a situation where the range of status IDs in two
different cached pages overlapped.

E.g., this log extract:

```
0: k: 110912736679636090, prev: 3521487, next: 3057175, size: 40, range: 112219564107059218..110912736679636090
1: k: 111651744569170291, prev: 3049659, next: 2710596, size: 40, range: 111926741634665808..111651744569170291
```

The range of IDs in page 0 overlaps with the range of IDs in page 1.

The previous `PageCache` assumed this couldn't happen, and broke in
various interesting ways when it did.

E.g., you can't find the page that contains a given status by looking
for the largest key less than the needle's status id. Given the pages
above looking for ID 112219564107059218 (first status in page 0) would
suggest page 1 as having the greatest key less than that ID. This
manifested as the correct page briefly appearing in the UI (page 0),
then being completely replaced with page 1.

Rewrite PageCache to fix this. The previous implementation used a single
`TreeMap` assuming items were always sorted by ID. The new code keeps an
unordered map from status IDs to the page that contains that status, and
a separate `LinkedList` that contains the pages in order they're
provided by the API. This decouples the ordering of pages in the cache
with the overall ordering of items within the pages.
2024-04-22 20:34:16 +02:00
Nik Clayton b8939dd2cf
fix: Ensure actionbar title is set correctly (#628)
https://github.com/pachli/pachli-android/pull/589 changed the initial
setting of the action bar title to use `binding.mainToolbar.title`
instead of `supportActionBar?.title`.

Not sure why, but this doesn't work on first use, and was showing
"Pachli Current" until the user changes tabs. Swap one usage back to
`supportActionBar?.title` to fix this, and update the other to do the
same thing to keep the code consistent.
2024-04-15 22:42:22 +02:00
Nik Clayton 7752cf9210
fix: Set correct text direction for localised error messages (#601)
Use `unicodeWrap` when inserting placeholders in error messages so they
set the correct text direction.

Update some strings with formatting directives to (a) include `_fmt`
in the name, and (b) use `%1$s` instead of `%s`.
2024-04-09 14:26:15 +02:00
Weblate (bot) 9e35147658
fix(l10n): Translations update from Hosted Weblate (#588)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pachli/Fastlane
Metadata](https://hosted.weblate.org/projects/pachli/fastlane-metadata/).


It also includes following components:

* [Pachli/Core/Network :
Main](https://hosted.weblate.org/projects/pachli/corenetwork-main/)

* [Pachli/App :
Main](https://hosted.weblate.org/projects/pachli/app-main/)

* [Pachli/Feature/About :
Main](https://hosted.weblate.org/projects/pachli/featureabout-main/)

* [Pachli/Feature/Lists :
Main](https://hosted.weblate.org/projects/pachli/featurelists-main/)

* [Pachli/App :
Google](https://hosted.weblate.org/projects/pachli/app-google/)

*
[Pachli/Feature/Login](https://hosted.weblate.org/projects/pachli/featurelogin/)

* [Pachli/Core/Designsystem :
Main](https://hosted.weblate.org/projects/pachli/coredesignsystem-main/)

* [Pachli/Core/Activity :
Orange](https://hosted.weblate.org/projects/pachli/coreactivity-orange/)

* [Pachli/Core/Activity :
Main](https://hosted.weblate.org/projects/pachli/coreactivity-main/)

* [Pachli/App :
Fdroid](https://hosted.weblate.org/projects/pachli/app-fdroid/)

* [Pachli/Core/Ui :
Main](https://hosted.weblate.org/projects/pachli/coreui-main/)



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pachli/fastlane-metadata/horizontal-auto.svg)

---------

Co-authored-by: ButterflyOfFire <boffire@users.noreply.hosted.weblate.org>
Co-authored-by: Kalle Kniivilä <kalle.kniivila@gmail.com>
Co-authored-by: Nik Clayton <nik@ngo.org.uk>
2024-04-05 17:52:39 +02:00
Nik Clayton b52ec248b0
refactor: Remove unnecessary whitespace in poll_info_format resource (#595) 2024-04-05 16:46:38 +02:00
Angelo Suzuki 6507e2bec1
fix: Setup compose button when creating MainActivity (#589)
The compose button isn't initially working on `MainActivity`. It is only
properly setup after changing tabs.

Fix by calling `MainActivity.refreshComposeButtonState(TabViewData)` on
creation.
2024-04-04 13:33:43 +02:00
Nik Clayton 6bef6f2fae
feat: Simplify adding/removing timelines from tabs (#587)
Previously, modifying any tabs meant opening the left-side nav, opening
Account preferences > Tabs, and then adding / removing tabs. This is
time consuming, and difficult for new users to discover.

In addition, it was possible to remove the Home tab, and there was a
hardcoded minimum of at least two tabs.

Fix this.

When viewing a timeline that is not already in a tab an "Add to tab"
menu item is enabled, which appends the timeline to the list of existing
tabs.

When viewing a timeline in a tab (that is not the Home timeline) a
"Remove tab" menu item is enabled, which removes the tab from the list
of existing tabs.

If the user removes the active tab (either with this menu item, or
through preferences) the tab to the left of the active tab becomes the
new active tab.

A new "Manage tabs" menu item is also provided, as a shortcut to the
existing Account preferences > Tabs screen.

When managing tabs the Home timeline can not be removed; the button to
remove it is removed, and swiping is disabled on that list item. The
restriction of "at least two tabs" has also been removed.

`NotificationsActivity` has been removed, as `TimelineActivity` can
display `NotificationsFragment`.

To make the three "Trending" types (hashtags, links, and posts) more
visually distinct add two new icons for links (ic_newspaper) and posts
(ic_whatshot).

Fixes #572, #584, #585, #569
2024-04-03 00:10:09 +02:00
Nik Clayton 0829d91989
fix: Stop crash in FollowedTagsActivity when view is not created (#586) 2024-04-02 17:51:46 +02:00
Nik Clayton 4ea42f4a2c
feat: Add left-nav entries for Local, Federated, and Direct Messages (#583)
Fixes #570
2024-04-01 21:42:28 +02:00
Nik Clayton d65d969257
fix: Allow filter expiration to be set to "indefinite" (#582)
Mastodon API uses an "empty" `expires_in` value for a filter to mean
"Does not expire" (i.e., indefinite).

This was modelled as a null. Which doesn't work, because Retrofit does
not send name/value pairs in encoded forms if the value is null.

Fix this by making the API type a `String?`, and explicitly using the
empty string when indefinite expiry is used. This has to be converted
back to an Int? in a few places.

See
https://github.com/mastodon/documentation/issues/1216#issuecomment-2030222940
2024-04-01 20:48:11 +02:00
Nik Clayton c45408088a
refactor: Use the `minutes` helper to create the duration (#581) 2024-04-01 20:14:19 +02:00
Nik Clayton 55c12dde44
refactor: Improve show/hide FAB logic (#580)
Previous code was inconsistent about how and when the FAB was hidden if
the user had set the relevant preference.

- Sometimes the FAB has hidden by setting the visibility to false, which
removed it with no animation.

- Sometimes the value of the preference was checked once, when the
fragment or activity was created.

- Some timelines didn't show the FAB (Hashtags, Favourites, Bookmarks,
TrendingLinks, TrendingStatuses).

- Logic for figuring out which `ComposeActivity` intent to use was
scattered across different files.

Improve this by:

- Expose changes to the `FAB_HIDE` preference in the relevant viewmodels
as a flow the UI component can collect.

- Centralise the show/hide logic in a new `ActionButtonScrollListener`
class, and always using `show()`/`hide()` to animate the transition.

- Centralise the logic for creating the `ComposeActivity` intent in
`TabViewData`.
2024-04-01 19:57:46 +02:00
Nik Clayton 53fa56bbb4
feat: Support adding "Favourites" to a tab (#578) 2024-03-31 00:03:32 +01:00
Nik Clayton 64fae0ceb6
refactor: Rename `StatusListActivity` to `TimelineActivity` (#577)
More accurately reflects what it does, and simplies the diffs for
upcoming work.
2024-03-30 23:48:04 +01:00
Nik Clayton 8257ded395
refactor: Remove `TabData` type (#576)
`TabData` recorded the type of the timeline the user had added to a tab.
`TimelineKind` is another type that records general information about
configured timelines, with identical properties.

There's no need for both, so remove `TabData` and use `TimelineKind` in
its place.

`TimelineKind` is itself mis-named; it's not just the timeline's kind
but also holds data necessary to display that timeline (e.g., the list
ID if it's a `.UserList`, or the hashtags if it's a `.Hashtags`) so
rename to `Timeline` to better reflect its usage. Move it to a new
`core.model` module.
2024-03-30 23:27:25 +01:00
Nik Clayton 09ca6ae0a8
fix: Create a dedicated menu for NotificationsActivity (#571)
Previous code (probably copy/paste error) used R.menu.activity_trending.
2024-03-29 21:37:45 +01:00
Nik Clayton c2db3dfbcc
chore: Prepare release 2.4.0 (versionCode 13) (#568) 2024-03-28 11:46:06 +01:00
Nik Clayton 0bb269a37d
fix: Don't crash on invalid avatars (#566)
Workaround a Glide bug where the error() handler is not always called,
in this case when the URL does not resolve to an image; for example, a
misconfigured server that redirects requests for the image to an HTML
page.

Catch the exception and use the default avatar image in these cases.
2024-03-24 19:32:28 +01:00
Nik Clayton d3c7c7c89a
fix: Surface all exceptions to the user instead of crashing (#565)
Previous code would handle some expected exceptions (IO, HTTP) when
fetching a timeline, and show them to the user. Any other exception
would crash.

Now, surface all exceptions. Treat IO and HTTP exceptions as retryable
and show the "Retry" option, all others are considered non-retryable.

Provide a specific error string for exceptions caused by bad JSON.
2024-03-24 18:36:28 +01:00
Nik Clayton 2a4126a542
fix: Improve URL / tag / mention extraction when composing (#564)
Previous code used custom regular expressions to extract URLs, hashtags,
and mentions from text while the user was writing a post. These were
inconsistent with the ones that Mastodon uses so the derived character
count could be wrong.

As well as being visually incorrect this could prevent the user from
posting a status that was within the length limit, or allow them to
attempt to post a status that was over the length limit (which would
then fail).

Fix this by dropping the homegrown regular expressions and using the
same text parsing library that Mastodon users; twitter-text. This has
been converted to Kotlin and the functionality related to Twitter
specific features has been removed.

The hashtag handling has been adjusted, as Mastodon is more permissive
about the positions where hashtags can appear than Twitter is, in
particular, a hashtag does not need to be preceded with whitespace if
the tag appears after some scripts, such as Hirigana.
2024-03-23 14:14:07 +01:00
Kalle Kniivilä d75c4354bb fix(l10n): Update Finnish translations
Currently translated at 100.0% (594 of 594 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/fi/
2024-03-21 17:43:55 +01:00
Nik Clayton 783d4e5cca
feat: Notify the user about severed relationships (#557) 2024-03-21 17:27:48 +01:00
Nik Clayton 8ef227fd20
fix: Improve accessibility of ComposeActivity bottomsheets and buttons (#548)
The previous bottomsheets did set a minimum height for the menu items,
so they were less than the recommended 48dp minimum. Fix that to improve
the overall accessibility.

Always highlight the "visibilty" icon, to make it clear that it's
something that is set (even if to the default).

Show the visibility icon on the "Toot" button as an additional reminder
to the user.

Other changes:

- Use the "filled" style for all icons (the visibility icons had the
"outlined" style)

- Use the `makeIcon` helper function.

- Use the `Status.Visibility` extension functions to determine the icon
for each visibility type, reducing code duplication.
2024-03-21 16:24:31 +01:00
Nik Clayton 778017475d
revert: Allow "Vote" button to expand to contain text (#545)
Expanded too far...
2024-03-19 19:02:15 +01:00
Nik Clayton ae811d7126
refactor: Allow "Vote" button to expand to contain text (#545) 2024-03-19 18:00:42 +01:00
Nik Clayton bcceaaa434
feat: Show the post's language tag in the detailed view (#546) 2024-03-19 18:00:33 +01:00
Nik Clayton c2fc3d1f08
feat: Allow user to see poll results before voting (#543)
Show a labelled checkbox to the bottom-right of polls that the user has
not voted in and that have votes. If checked the current vote tally (as
percentages) will be shown, along with a bar showing the relative value
of each option.
2024-03-19 13:23:21 +01:00
Kalle Kniivilä 4ca43e1c2e fix(l10n): Update Finnish translations
Currently translated at 100.0% (593 of 593 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/fi/
2024-03-18 19:31:42 +01:00
Nik Clayton 9e0a1f4015
refactor: Simplify use of BackgroundMessageView (#539)
Previous code expected callers to typically provide the drawable and the
error message string resource, resulting in duplicate code at many
callsites.

Replace with three canned messages for empty containers, generic errors,
and network errors respectively. The images for these are fixed, the
caller may choose a different string resource for the error if there is
a more specific option.

Update and simplify the call sites.
2024-03-16 22:12:54 +01:00
Nik Clayton e41722e16f
refactor: Extract user list functionality to `feature:lists` (#537)
Move `ListsActivity`, along with fragments and viewmodels, to a new
`feature:lists` module.

Previous code used the `item_follow_request` layout, which was not
ideal, so update it to use a dedicated layout, `item_account_in_list`.

The UI uses strings and views originally defined in the main app, so
move them elsewhere so they can be re-used.

- `BackgroundMessageView` moves to `core.ui`.

- `Lazy` moves to `core.common`.

- `ThrowableExtensions` split; the extensions specific to throwables
from network activity move to `core.network`, others move to `core.ui`.

- `BindingHolder` moves to `core.ui`

- Shared drawables and strings move to `core.ui`.
2024-03-16 18:42:11 +01:00
Nik Clayton 9535506596
fix: Catch and swallow snackbar exception (#536)
For reasons not fully understood the root of a fragment's view might not
have relevant view from the activity set as its parent. This causes
`Snackbar.make()` to throw an exception, and crash.

See https://issuetracker.google.com/issues/228215869.

For now, "fix" this by swallowing the exception. Not showing the error
is better than crashing.
2024-03-16 14:34:21 +01:00
Nik Clayton bdbe2f85c2
feat: Allow the user to set a list's replies policy (#534)
The replies policy controls whether replies from members of the list
also appear in the list.

Display the replies policy as three radio buttons when a list is created
or updated, and send the chosen replies policy via the API.

Default value if not specified is always "list", for consistency with
the Mastodon API defaults.

While I'm here:

- Ensure the list dialog layout is inflated using the dialog's themed
context
- Use a `TextInputLayout` wrapper around the list name in the list
dialog for better UX
- Simplify the dialog layout, use LinearLayout, and standard padding and
margins
2024-03-14 23:56:16 +01:00
Nik Clayton 6c970a9742
fix: Keep TabDataJsonAdapter so Moshi can deserialize in release builds (#535) 2024-03-14 20:10:34 +01:00
Nik Clayton 256c6139ac
feat: Show network activity in ListsActivity (#533)
Modify `ListsViewModel` to keep track of the number of active network
operations and export as a flow. Collect this in `ListsActivity` and
show a `LinearProgressIndicator` while it is non-zero.
2024-03-14 18:18:34 +01:00
Nik Clayton d66d9d32a4
refactor: Prune cached media using a worker (#529)
Previous code would prune any cached media every time `MainActivity` was
created, causing extra IO just as the user is trying to use the app.

Re-implement as a WorkManager worker so it can run when the device is
idle, without interfering with the responsiveness of the device.
2024-03-14 13:31:18 +01:00
Nik Clayton 6f8a3f20a9
chore: Add TODO for more specific error message in the future (#528) 2024-03-14 12:32:23 +01:00
Angelo Suzuki 5be93acf6b
feat: Improve Image Viewer toolbar auto-hide (#521)
Cancel the auto-hide behavior in case the toolbar menu items are
interacted with.

Improves https://github.com/pachli/pachli-android/issues/505,
https://github.com/pachli/pachli-android/issues/507

---------

Co-authored-by: Nik Clayton <nik@ngo.org.uk>
2024-03-13 20:19:46 +01:00
Nik Clayton d9fc664a63
refactor: Remove obsolete emoji-related resources (#526) 2024-03-13 11:29:45 +01:00
Nik Clayton cfc15c8f64
refactor: Use LinearProgressIndicator with delayed show/hide (#525)
Previous code used a normal ProgressBar and a coroutine to delay
hiding/showing the bar for a snappier UI perception.

This is built-in functionality in LinearProgressIndicator, so switch to
that.

While I'm here, implement the "Select list" dialog's layout as a layout
resource.
2024-03-13 10:03:10 +01:00
Nik Clayton 006c358053 fix: Sort lists by title wherever they occur 2024-03-12 19:52:45 +01:00
Nik Clayton 7da4bc090b fix: Update tabs when lists are renamed or deleted
If the user has tabs containing one or more lists, and any of those
lists are renamed or deleted then the change should be reflected in the
tabs.

To do that:

`MainActivity`:

- Re-create tabs whenever lists are loaded and there's a list in a tab
- Compare lists-in-tabs by the ID of the list when restoring the user's
  tab, so that a list rename doesn't lose their position.

`NetworkListsRepository`:

- Update the user's tab preferences whenever lists are loaded, removing
  tabs that contain lists that have been deleted, and updating the
  list's title for lists that have been renamed.

Fixes #192
2024-03-12 19:52:45 +01:00
Nik Clayton a973f67ac8 refactor: Store tab preferences as polymorphic JSON 2024-03-12 19:52:45 +01:00
Nik Clayton 805a2a9b1d feat: Show "Followed hashtags" menu item in left-nav 2024-03-11 21:31:34 +01:00
Nik Clayton 65a0fb19d9 feat: Tapping followed hashtag names in lists opens relevant timeline 2024-03-11 21:31:34 +01:00
Nik Clayton 4762411147
refactor: Replace custom worker factory with HiltWorkerFactory (#517)
Removes the need for a separate `WorkerModule` and factory methods in
each worker.
2024-03-11 10:49:58 +01:00
Nik Clayton 442f3bc80c
feat: Show user's lists in the left-side navigation menu (#514)
Previously to view a list the user either had to add it to a tab, or tap
through "Lists" in the navigation menu to their list of lists, and then
tap the list they want.

Fix that, and show all their lists in a dedicated section in the menu,
with a new "Manage lists" entry that's functionality identical to the
old "Lists" entry (i.e., it shows their lists and allows them to create,
delete, and edit list settings).

To do that:

- Implement a proper `ListsRepository` as the single source of truth for
list implementation throughout the app. Expose the current list of lists
as a flow, with methods to perform operations on the lists.

- Collect the `ListsRepository` flow in `MainActivity` and use that to
populate the menu and update it whenever the user's lists change.

- Rewrite the activities and fragments that manipulate lists to use
`ListRepository`.

- Always show error snackbars when list operations fail. In particular,
the HTTP code and error are always shown.

- Delete the custom `Either` implementation, it's no longer used.

- Add types for modelling API responses and errors, `ApiResponse` and
`ApiError` respectively. The response includes the headers as well as
the body, so it can replace the use of `NetworkResult` and `Response`.
The actual result of the operation is expected to be carried in a
`com.github.michaelbull.result.Result` type. Implement a Retrofit call
adapter for these types.

Unit tests for these borrow heavily from
https://github.com/connyduck/networkresult-calladapter

Additional user-visible changes:

- Add an accessible "Refresh" menu item to `ListsActivity`.

- Adding a list to a tab has a dialog with a "Manage lists" option.
Previously that would close the dialog when clicked, so the user had to
re-open it on returning from list management. Now the dialog stays open.

- The soft keyboard automatically opens when creating or editing a list.
2024-03-10 23:14:21 +01:00
Nik Clayton a4dc3b85bd
fix: Show sized placeholder for hidden account media (#516)
Previous code showed a small icon for account media that the user has
hidden.

Now determine the correct size / aspect ratio for the media and use that
to compute the placeholder (either a blurhash, or the link colour for
consistency with the view on a timeline).

Fixes #513
2024-03-10 23:13:58 +01:00
Angelo Suzuki bdf2d9329e
feat: Auto-hide Image Viewer toolbar (#507)
Previous code showed the toolbar and caption when displaying the image,
and the user has to tap the screen to dismiss it.

New code is consistent with the video viewer; the toolbar and caption
is displayed for two seconds, and then auto-hides.

- Tapping after the hide displays the toolbar and caption, which will
  not auto-hide and requires a second tap to dismiss.

- Tapping the caption before it's hidden cancels the auto-hide

Fixes #505
2024-03-10 23:13:40 +01:00
Nik Clayton 0445e187df
fix: Ensure logging out accounts completes (#515)
The account logout process could fail due to API exceptions; network
errors for example, or if the user had already revoked the app's token
for that account. This would prevent the rest of the logout process
(cleaning database, etc) from completing.

Fix this by ignoring network errors during the logout process, and
always cleaning up account content in the database.

Fix a related issue where a deleted account might be recreated in a
partial state if the account's visible position was saved after it was
deleted. The recreated account couldn't do anything as it had no tokens,
but is very confusing.
2024-03-10 12:25:12 +01:00
Nik Clayton 32a6d3465b
feat: Include "Notifications" in the left-side navigation menu (#504)
Previously the only way to access notifications was to dedicate a tab to
them. Now notifications are available from the left-side navigation menu
so they're always accessible.

Add them to the top of the list, and swap the order of bookmarks and
favourites, assuming that users are more likely to want to see their
bookmarks than their favourites.

Move "Edit profile" to the bottom with the other settings options,
assuming that editing their profile does not happen very often, so
should not be at the top of the list.
2024-03-05 14:45:28 +01:00
Juan M Sevilla 92eb9f32b7 fix(l10n): Update Spanish translations
Currently translated at 100.0% (623 of 623 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/es/
2024-03-04 20:33:08 +01:00
Nik Clayton dc23ea23d1
refactor: Remove last vestiges of rxJava (#492) 2024-03-03 17:38:32 +01:00
Nik Clayton da6e026cc6
refactor: Implement updateShortcut as a suspending function (#491)
Previously a regular function that created and subscribed to an rxJava
callable, now it's a suspending function that enforces Dispatchers.IO as
the context, launched in its own coroutine.
2024-03-03 17:21:04 +01:00
Nik Clayton 900b1728bf
refactor: Implement deleteStaleCachedMedia as a suspending function (#490)
Previously a regular function launched with an rxJava scheduler, now
it's a suspending function that enforces Dispatchers.IO as the context,
launched in its own coroutine.
2024-03-03 17:02:23 +01:00
Nik Clayton 1026fccc40
refactor: Restructure ViewMediaActivity and related fragments (#489)
Highlights:

- Implement fragment transitions for video to improve the UX, video
won't start playing until the transition completes
- Remove rxJava
- Move duplicate code in to base classes

Details:

`MediaActionsListener`:

- Move to `ViewMediaFragment` as it's used by both subclasses
- Remove need for separate `VideoActionsListener`
- Rename methods to better reflect their purpose and improve readability

`ViewMediaFragment`:

- Move duplicated code from `ViewImageFragment` and `ViewVideoFragment`
- Rewrite code that handles fragment transitions to use a
`CompleteableDeferred` instead of `BehaviorSubject` (removes rxJava).
- Rename methods and properties to better reflect their purpose and
improve readability
- Add extra comments

`ViewImageFragment`:
- Rewrite code that handles fragment transitions to use a
`CompleteableDeferred` instead of `BehaviorSubject` (removes rxJava).

`ViewVideoFragment`:

- Implement fragment transitions for video to improve the UX, video
won't start playing until the transition completes
- Manage toolbar visibility with a coroutine instead of a handler
- Add extra comments

`ViewMediaActivity`:
- Rename properties to better reflect their purpose and improve
readability
- Add extra comments

`ImagePagerAdapter`:
- Rename properties to better reflect their purpose and improve
readability
- Add extra comments
2024-03-03 16:07:39 +01:00
Nik Clayton 72e5ca887d
fix(deps): update agp to v8.3.0, lint to 31.3.0 (#483)
New lint rules highlighted a potential crash; the use of named match
groups (used here when extracting server versions) requires API >= 26 or
throws an exception.

Use the group numbers instead of names when extracting the value, but
keep the group names in the regular expressions for readability.
2024-03-01 23:07:14 +01:00
Nik Clayton b9512e49b4
fix: Ensure files are fully downloaded before sharing (#482)
The previous code would share media by either:

a. If it was an image, downloading the image using Glide in to a bitmap,
then recompressing as a PNG, saving, and sharing the resulting file.

b. Otherwise, create a temporary file, enqueue a DownloadManager request
to download the media in to the file, and immediately start sharing,
hoping that the download had completed in time.

Both approaches have problems:

In the "image" case the image was being downloaded (or retrieved from
the Glide cache), decompressed to a bitmap, then recompressed as a PNG.
This uses more memory, and doesn't share the original contents of the
file. E.g., if the original file was a JPEG that's lost (and the PNG
might well be larger than the source image).

In the second case the DownloadManager download is not guaranteed to
have completed (or even started) before the user chooses the share
destination. The destination could receive a partial or even empty file.

Fix both of those cases by always fully downloading the file before
sending the share intent. This guarantees the file is available to
share, and in its original format. Since this uses the same OkHttpClient
as the rest of the app the content is highly likely to be in the OkHttp
cache, so there will no extra network traffic because of this.
2024-03-01 21:54:30 +01:00
Nik Clayton fcae44110b
refactor: Remove synchronous search functions (#481)
The previous code used synchronous (i.e., non-suspending) functions to
call the /api/v2/search and /api/v2/accounts/search endpoints.

This is not necessary as the search was always performed in a separate
thread.

Remove, and replace their usage with the equivalent functions that
suspend.
2024-03-01 18:55:59 +01:00
Nik Clayton fb66293713
refactor: Remove duplicate strings from Filter.Action (#479)
Simplify code that users `Filter.Action` by deserialising directly into
the type instead of storing / using strings throughout the API.
2024-02-29 09:49:17 +01:00
Nik Clayton af58de5a8f
refactor: Enable core library desugaring as build convention logic (#480)
Once desugaring is enabled it needs to be enabled for up/down the
dependency chain, so enable it in the shared configuration defined by
the build convention code.

Highlighted a failing test that wasn't being run, so fix that too.
2024-02-29 09:43:44 +01:00
Nik Clayton 9a23439d04
refactor: Remove duplicate strings from Filter contexts (#478)
A filter's context (previously referred to as its `kind`) controls where
the filter is applied.

This was implemented as an enum with a specific property to control how
it would serialise when @FormUrlEncoded, and with a @Json annotation for
Moshi.

In addition, the model objects kept the filter context in its string
form throughout Pachli, requiring periodic conversion to/from the enum
type, making the code more complicated.

Fix this, by:

1. Converting the incoming JSON value to the enum type immediately, so
the rest of the code uses the enum constants exclusively.

2. Implement a Retrofit converter that serialises the enum value when
@FormUrlEncoded to the same string used in JSON serialisation
2024-02-28 19:09:54 +01:00
Nik Clayton 2aa01fba8c
chore: Prepare release 2.3.0 (versionCode 12) (#477) 2024-02-28 09:54:52 +01:00
Nik Clayton d943fa1aca
fix: Update InstanceV1/V2 related types based on real-world usage (#476)
Many servers that claim to be Mastodon-API compatible are not, as
evidenced by the content they include in the responses to
`/api/v1/instance` and `/api/v2/instance` requests.

Work around the worst of the breakage by providing defaults or marking
some fields as nullable (with a default null).

Bugs have been reported against the relevant projects.
2024-02-28 00:02:03 +01:00
Kalle Kniivilä 58a6f75728 fix(l10n): Update Finnish translations
Currently translated at 100.0% (622 of 622 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/fi/
2024-02-27 10:37:41 +01:00
Martijn de Boer 44365f445f fix(l10n): Update Dutch translations
Currently translated at 100.0% (2 of 2 strings)

Translation: Pachli/App : Google
Translate-URL: https://hosted.weblate.org/projects/pachli/app-google/nl/
2024-02-25 15:53:32 +01:00
Martijn de Boer c75c143825 fix(l10n): Update Dutch translations
Currently translated at 100.0% (622 of 622 strings)

Translation: Pachli/App : Main
Translate-URL: https://hosted.weblate.org/projects/pachli/app-main/nl/
2024-02-25 15:53:32 +01:00
Martijn de Boer edcf6f13ab fix(l10n): Update Dutch translations
Currently translated at 100.0% (2 of 2 strings)

Translation: Pachli/App : Fdroid
Translate-URL: https://hosted.weblate.org/projects/pachli/app-fdroid/nl/
2024-02-25 15:53:32 +01:00
Nik Clayton 51c64a74dc
fix: Parse roles without a `highlighted` property (#467)
The `highlighted` property on a role may be absent. If it is this breaks
account parsing, including accounts in an /api/v2/instance response.
This, in turn, breaks determining server capabilities, including whether
or not translation is supported.

Set the default to `true`, which matches observed Mastodon behaviour.
2024-02-22 21:17:34 +01:00
Nik Clayton 415b182405
change: Log the trigger for a refresh/reload (#466)
A user is reporting that a refresh is happening in the middle of loading
content, borne out by the existing logs.

Those logs don't say what has triggered the refresh attempt, so add
additional logging whenever a refresh can occur to record what triggered
it.
2024-02-22 17:41:56 +01:00
Nik Clayton 9071a89e48
feat: Display uncropped media on account media pages (#464)
Previously media on the "Media" tab was displayed scaled and cropped to
a square aspect ratio, effectively forcing the user to tap every image
to see it.

Now, display the images scaled but not cropped, layed out with
`StaggeredGridLayoutManager`. This shows each image in full (still
scaled) providing a better experience when scrolling down.

Scrolling up can occasionally introduce gaps in the grid as images are
re-placed as viewholders are reused. When this happens images animate to
a better position when scrolling stops.
2024-02-22 14:33:44 +01:00
Nik Clayton 203784d718
fix: Don't crash when displaying update check dialog (#463)
Previous code injected `ApplicationContext`, which is not themed, and
caused a crash if the "update" dialog was shown.

Fix by passing the necesssary context in explicitly when the check is
performed.
2024-02-22 14:16:05 +01:00
Nik Clayton 2162e03e1f
fix: Handle JSON enums with unknown values (#462)
Previous code expected all incoming enums values to map directly to
Kotlin enum constants.

This is a problem for servers with additional features -- e.g.,
"reaction" as a notification type.

Fix this with a new Moshi adapter that will set the incoming value to a
given constant if it's not recognised.

Apply this to the enum constants in core.network to ensure they are
handled.

Clean up enum handling in Converters.kt, ComposeViewModel.kt, and
Status.kt by using the existing `.ordinal` property and some extension
functions for idiomatic code.

Fixes #461
2024-02-21 23:36:24 +01:00
Nik Clayton 941f4677eb
fix: Show previews for playable audio media from accounts (#460)
Previous code showed a generic placeholder for audio media on the
account's "Media" tab.

Fix this so the preview image is shown (if it's available).

- Move the "is this attachment previewable?" code to `Attachment` so it
can be reused here.

- Restructure the logic in `AccountMediaGridAdapter` to use the new
`isPreviewable()` method when deciding whether to show a preview.

- Attachments have dedicated placeholder drawables, use those when the
preview is not available.
2024-02-20 16:20:34 +01:00