- Move all database queries off the ui thread - this is a massive
performance improvement
- ViewModel for MainActivity - this makes MainActivity smaller and
network requests won't be retried when rotating the screen
- removes the Push Notification Migration feature. We had it long
enough, all users who want push notifications should be migrated by now
- AccountEntity is now immutable
- converted BaseActivity to Kotlin
- The header image of Accounts is now cached as well
Hilt is an annotation processor built on top of Dagger which allows to
remove all the Android dependency injection boilerplate code (currently
around 900 lines) by writing it for us.
Hilt can use KSP instead of Kapt so Kapt can be completely removed from
the project. Kapt is slow, deprecated and has a few compatibility
issues. Removing Kapt will improve build times since no Java stubs have
to be generated for Kotlin classes anymore (Note that KSP also processes
annotations in Java classes so it can completely replace Kapt).
- Remove all modules related to manual dependency injection
configuration.
- Rename `AppModule` to `StorageModule` since it now only contains
configuration to retrieve the DataBase and SharedPreferences.
- Annotate all entry points (Activities, Fragments, BroadcastReceivers
and Services) with `@AndroidEntryPoint`.
- Annotate all injected ViewModels with `@HiltViewModel` and replace the
custom ViewModel Factory with the default one (which integrates with the
one generated by Hilt).
- Add a public field to allow overriding the default
ViewModelProvider.Factory in `BaseActivity` in tests.
- Annotate tested Activities with `@OptionalInject` since Activity tests
currently rely on the Activities not being injected automatically.
- Annotate injected `Context` arguments with `@ApplicationContext`. Hilt
provides the `Context` binding automatically but requires to specify if
the Application or Activity Context is wanted.
- Add WorkManager Hilt integration so all Workers are injected by Hilt
automatically using `HiltWorkerFactory`.
- Lazily initialize WorkManager in `TuskyApplication`.
- Remove Kapt and Kapt workarounds.
- ~~Remove toolchain configuration for Java 21. Toolchains force the
Java bytecode to match the JDK version used to build the project, and
apparently Hilt doesn't run inside the toolchain so cannot process the
source code if the JDK version of the toolchain is higher than the JDK
used to run Gradle. [And configuring a toolchain for an older Java
version causes other
issues](https://jakewharton.com/gradle-toolchains-are-rarely-a-good-idea/).
**Removing toolchains configuration doesn't prevent the project from
being built using JDK 21** or more recent versions but allows to build
the project using older JDKs as well.~~
Added a fix to allow Hilt to properly use the JDK toolchain.
- ~~Set the Java and Kotlin bytecode target to Java 17. The standard
bytecode target for Android projects is usually Java 8 or 11 (any higher
version doesn't provide any benefit but may cause compatibility issues).
However, since the app currently uses a library built against Java 17
bytecode (`networkresult-calladapter`), it needs to target at least Java
17 bytecode as well.~~
- Update the Dagger 2 URL in the licenses screen. Hilt is part of Dagger
2 so the label wasn't changed.
- Remove empty file `ExampleInstrumentedTest.java`.
- Replace deprecated `MigrationTestHelper` constructor call.
- Add reified inline extension methods for `Bundle` and `Intent` to
retrieve `Parcelable` and `Serializable` objects by calling the core
`BundleCompat` and `IntentCompat` methods, to allow shorter syntax and
removing the need to pass the class explicitly.
- Replace deprecated `drawable.setColorFilter()` with simpler
`drawable.setTint()` (uses blend mode `SRC_IN` by default, has the same
effect as `SRC_ATOP` when the source is a color).
- Rename shadowed variables (mostly caught exceptions).
- Remove unnecessary `.orEmpty()` on non-null fields.
- Replace `.size() == 0` with `.isEmpty()`.
- Prevent `NullPointerException` when `account.getDisplayName()` is
`null` in `StatusBaseViewHolder.setDisplayName()`.
- Declare `customEmojis` argument as non-null in
`StatusBaseViewHolder.setDisplayName()` because it calls
`CustomEmojiHelper.emojify()` which now requires it to be non-null.
- Prevent `NullPointerException` when no matching filter is found in
`StatusBaseViewHolder.setupFilterPlaceholder()`.
- Remove deprecated call to `setTargetFragment()` (target fragment is
not used anyway).
- Remove deprecated call to `isUserVisibleHint()` and test if the view
has been destroyed instead.
- Remove some unused imports.
- Remove unnecessary casts.
- Rename arguments to supertype names when a warning is shown.
- Prevent a potential memory leak by clearing the
`toolbarVisibilityDisposable` reference in `onDestroyView()`.
The app currently uses a custom callback system for requesting
permissions, located in `BaseActivity`.
This code doesn't work when the activity gets re-created before the
permission is granted: the callback will be lost with the Activity and
no action will be performed after the permission is granted.
To avoid these issues while still simplifying the code, Google
recommends to use the ActivityResultContract APIs to request
permissions, which allow to register persistent callbacks. This pull
request removes the legacy API and replaces the calls with
`registerForActivityResult(ActivityResultContracts.RequestPermission())`.
- `ActivityResultContracts.RequestPermission` will check if the
permission is already granted and call the callback synchronously when
possible. So this check doesn't need to be performed anymore.
- In `SearchStatusesFragment` and `SFragment`, the download action to
launch after granting the permission requires an argument (a list of
media URLs). This argument is temporarily stored in a Fragment field and
saved and restored as part of the instance state, so the action can
properly resume after an Activity re-creation.
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.
builds upon work from #4082
Additionally fixes some deprecations and adds support for [predictive
back](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture).
I also refactored how the activity transitions work because they are
closely related to predictive back. The awkward
`finishWithoutSlideOutAnimation` is gone, activities that have been
started with slide in will now automatically close with slide out.
To test predictive back you need an emulator or device with Sdk 34
(Android 14) and then enable it in the developer settings.
Predictive back requires the back action to be determined before it
actually occurs so the system can play the right predictive animation,
which made a few reorganisations necessary.
closes#4082closes#4005
unlocks a bunch of dependency upgrades that require sdk 34
---------
Co-authored-by: Goooler <wangzongler@gmail.com>
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>
```
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.keylesspalace.tusky.test/com.keylesspalace.tusky.ViewMediaActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Caused by: java.lang.NullPointerException
at com.keylesspalace.tusky.ViewMediaActivity.adjustScreenWakefulness(ViewMediaActivity.kt:351)
at com.keylesspalace.tusky.ViewMediaActivity.onCreate(ViewMediaActivity.kt:161)
at android.app.Activity.performCreate(Activity.java:7009)
at android.app.Activity.performCreate(Activity.java:7000)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
```
* Support a swipe down to dismiss video
Images can already be dismissed with a swipe, this adds the same
functionality to videos.
- Add a VideoActionsListener interface for the hosting activity to dismiss
the fragment
- Add a gesture listener for swipes
- Dismiss the fragment if a swipe has a greated Y component than X
Fixes https://github.com/tuskyapp/Tusky/issues/2833
* Scale the video view when dragging
Provides identical visual feedback to the same operation on images.
* update to Api 33, fix some deprecations
* fix deprecated serializable/parcelable methods
* ask for notification permission
* fix code formatting
* add back comment in PreferencesActivity
* initial class setup
* handle events and filters
* handle status state changes
* code formatting
* fix status filtering
* cleanup code a bit
* implement removeAllByAccountId
* move toolbar into fragment, implement menu
* error and load state handling
* fix pull to refresh
* implement reveal button
* use requireContext() instead of context!!
* jump to detailed status
* add ViewThreadViewModelTest
* fix ktlint
* small code improvements (thx charlag)
* add testcase for toggleRevealButton
* add more state change testcases to ViewThreadViewModel
* migrating to ViewBinding part 4: Fragment
* fix imports
* don't use viewBinding extension in ViewImage and ViewVideoFragment
* don't use viewBinding extension in ViewImage and ViewVideoFragment
* Open photos embedded in preview cards in the internal image viewer instead of opening the browser
* Enable toolbar for single image viewer
* Apply review feedback
* Resolving some issues with media playback.
* Changing ViewVideoFragment so that it fully handles autohiding the
media controller, allowing consistency between it and the toolbar
* Fixed an issue where the toolbar and description were not fading in
* Do not hide the toolbar/media player if the video is loading or paused
* Created "ExposedPlayPausedVideoView" to allow hooking play/pause
* Fix back button when viewing videos
* Back button did not work if video controls were visible.
* Tweak audio player
* Always show the controls when audio begins playing
* Do not auto-hide the controls if the player is playing audio
* Address PR comments
* Use overloaded constructor
* Remove m prefix
* Fix subtle media player issues
* Fix audio player incorrectly auto-hiding after hiding/showing toolbar
* Only subscribe touch listener once content is ready
- Prevents top toolbar visibility from getting out of phase with audio player visibility if hidden during load
* upgrade api level to Android 10, resolve compile errors
* use androidx.preference.PreferenceManager instead of deprecated platform class
* add hyphenation to important TextViews
* setBottomSheetCallback -> addBottomSheetCallback
* implement new sharing api
* improve TuskyTileService so it shows account picker when multiple accounts are present
* delete unused AccountChooserService
* fix test
* improve ShareShortcutHelper
* remove debug log statement
* improve image loading fallback behavior in ShareShortcutHelper
* improve behavior on foldable devices
* Replace Picasso library with Glide library tuskyapp#1082
* Replace Picasso library with Glide library tuskyapp#1082
* Update load emoji with glide
* Update context used for Glide
* Removed unused import
* Replace deprecated SimpleTarget with CustomTarget
* Fix crash at the view image fragment, remove override image size
* Replace Single.create with Single.fromCallable
* View image fragment refactor
* Fix after merge
* Try to load cached image first and show progress view on failure
* Try to load cached image first and show progress view on failure
* upgrade to AndroidX, upgrade libraries
* move to MaterialComponents theme
* make sure the compose button looks good everywhere
* fix tollbar title/button alignment on tablet
* move to new material color theming, consolidate colors and themes
* fix build, fix imports
* set error on TextInputLayout instead of EditText
* fix imports, TootButton when
* improve snackbar style
* fix task description color
* Migrate ImagePagerAdapter to kotlin
* Migrate ViewMediaFragment to kotlin
* Make images and videos share the same activity/pager
* Show descriptions above videos
* Cleanup
* Address code review feedback
* Migrate media fragments to constraint layout