From 5192fb08a528add8381371992b6f838eb5a52a7d Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Thu, 4 Jan 2024 17:00:55 +0100 Subject: [PATCH] upgrade ktlint plugin to 12.0.3 (#4169) 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 --- .editorconfig | 12 +- .../com/keylesspalace/tusky/AboutActivity.kt | 14 +- .../tusky/AccountsInListFragment.kt | 58 +++- .../tusky/BottomSheetActivity.kt | 16 +- .../tusky/EditProfileActivity.kt | 35 ++- .../com/keylesspalace/tusky/ListsActivity.kt | 9 +- .../com/keylesspalace/tusky/MainActivity.kt | 92 ++++-- .../keylesspalace/tusky/StatusListActivity.kt | 73 ++++- .../java/com/keylesspalace/tusky/TabData.kt | 19 +- .../tusky/TabPreferenceActivity.kt | 32 +- .../keylesspalace/tusky/TuskyApplication.kt | 7 +- .../keylesspalace/tusky/ViewMediaActivity.kt | 42 ++- .../tusky/adapter/AccountFieldEditAdapter.kt | 11 +- .../tusky/adapter/AccountSelectionAdapter.kt | 5 +- .../tusky/adapter/EmojiAdapter.kt | 11 +- .../tusky/adapter/FollowRequestViewHolder.kt | 20 +- .../tusky/adapter/LocaleAdapter.kt | 6 +- .../tusky/adapter/PollAdapter.kt | 5 +- .../adapter/PreviewPollOptionsAdapter.kt | 6 +- .../adapter/ReportNotificationViewHolder.kt | 32 +- .../keylesspalace/tusky/adapter/TabAdapter.kt | 6 +- .../tusky/appstore/CacheUpdater.kt | 2 +- .../keylesspalace/tusky/appstore/Events.kt | 5 +- .../keylesspalace/tusky/appstore/EventsHub.kt | 4 +- .../components/account/AccountActivity.kt | 100 +++++-- .../components/account/AccountFieldAdapter.kt | 24 +- .../components/account/AccountPagerAdapter.kt | 6 +- .../components/account/AccountViewModel.kt | 25 +- .../account/list/ListSelectionFragment.kt | 14 +- .../account/list/ListsForAccountViewModel.kt | 2 +- .../account/media/AccountMediaFragment.kt | 28 +- .../account/media/AccountMediaGridAdapter.kt | 43 ++- .../accountlist/AccountListFragment.kt | 25 +- .../accountlist/adapter/AccountAdapter.kt | 4 +- .../accountlist/adapter/BlocksAdapter.kt | 17 +- .../adapter/FollowRequestsHeaderAdapter.kt | 16 +- .../accountlist/adapter/MutesAdapter.kt | 17 +- .../announcements/AnnouncementAdapter.kt | 25 +- .../announcements/AnnouncementsActivity.kt | 10 +- .../announcements/AnnouncementsViewModel.kt | 6 +- .../components/compose/ComposeActivity.kt | 282 ++++++++++++++---- .../compose/ComposeAutoCompleteAdapter.kt | 4 +- .../components/compose/ComposeViewModel.kt | 46 ++- .../components/compose/ImageDownsizer.kt | 8 +- .../components/compose/MediaPreviewAdapter.kt | 10 +- .../tusky/components/compose/MediaUploader.kt | 31 +- .../compose/dialog/AddPollDialog.kt | 12 +- .../compose/dialog/AddPollOptionsAdapter.kt | 11 +- .../compose/dialog/CaptionDialog.kt | 19 +- .../components/compose/dialog/FocusDialog.kt | 15 +- .../compose/view/ComposeOptionsView.kt | 5 +- .../compose/view/ComposeScheduleView.kt | 3 +- .../compose/view/FocusIndicatorView.kt | 12 +- .../components/compose/view/TootButton.kt | 5 +- .../conversation/ConversationAdapter.kt | 19 +- .../conversation/ConversationEntity.kt | 46 ++- .../ConversationLoadStateAdapter.kt | 11 +- .../conversation/ConversationsFragment.kt | 32 +- .../ConversationsRemoteMediator.kt | 5 +- .../conversation/ConversationsViewModel.kt | 8 +- .../domainblocks/DomainBlocksAdapter.kt | 11 +- .../domainblocks/DomainBlocksFragment.kt | 10 +- .../domainblocks/DomainBlocksViewModel.kt | 2 +- .../tusky/components/drafts/DraftHelper.kt | 18 +- .../components/drafts/DraftMediaAdapter.kt | 9 +- .../tusky/components/drafts/DraftsActivity.kt | 18 +- .../tusky/components/drafts/DraftsAdapter.kt | 9 +- .../components/drafts/DraftsViewModel.kt | 8 +- .../components/filters/EditFilterActivity.kt | 37 ++- .../components/filters/EditFilterViewModel.kt | 25 +- .../components/filters/FilterExtensions.kt | 4 +- .../components/filters/FiltersActivity.kt | 20 +- .../components/filters/FiltersAdapter.kt | 9 +- .../components/filters/FiltersViewModel.kt | 34 ++- .../followedtags/FollowedTagsActivity.kt | 10 +- .../followedtags/FollowedTagsAdapter.kt | 23 +- .../followedtags/FollowedTagsViewModel.kt | 10 +- .../instanceinfo/InstanceInfoRepository.kt | 20 +- .../tusky/components/login/LoginActivity.kt | 8 +- .../components/login/LoginWebViewActivity.kt | 2 +- .../components/login/LoginWebViewViewModel.kt | 14 +- .../notifications/NotificationFetcher.kt | 34 ++- .../notifications/NotificationsFragment.kt | 0 .../notifications/PushNotificationHelper.kt | 49 ++- .../preference/AccountPreferencesFragment.kt | 26 +- .../preference/PreferencesActivity.kt | 10 +- .../preference/PreferencesFragment.kt | 10 +- .../preference/ProxyPreferencesFragment.kt | 5 +- .../tusky/components/report/ReportActivity.kt | 22 +- .../components/report/ReportViewModel.kt | 9 +- .../tusky/components/report/Screen.kt | 2 +- .../report/adapter/StatusViewHolder.kt | 56 +++- .../report/adapter/StatusesAdapter.kt | 18 +- .../report/adapter/StatusesPagingSource.kt | 9 +- .../report/fragments/ReportNoteFragment.kt | 6 +- .../fragments/ReportStatusesFragment.kt | 24 +- .../report/model/StatusViewState.kt | 32 +- .../scheduled/ScheduledStatusActivity.kt | 11 +- .../scheduled/ScheduledStatusAdapter.kt | 21 +- .../scheduled/ScheduledStatusViewModel.kt | 2 +- .../tusky/components/search/SearchActivity.kt | 8 +- .../components/search/SearchViewModel.kt | 37 +-- .../search/adapter/SearchAccountsAdapter.kt | 12 +- .../search/adapter/SearchHashtagsAdapter.kt | 5 +- .../search/adapter/SearchStatusesAdapter.kt | 12 +- .../fragments/SearchAccountsFragment.kt | 4 +- .../search/fragments/SearchFragment.kt | 22 +- .../fragments/SearchStatusesFragment.kt | 50 +++- .../components/timeline/TimelineFragment.kt | 36 ++- .../timeline/TimelinePagingAdapter.kt | 9 +- .../timeline/TimelineTypeMappers.kt | 6 +- .../components/timeline/util/TimelineUtils.kt | 10 +- .../viewmodel/CachedTimelineRemoteMediator.kt | 8 +- .../viewmodel/CachedTimelineViewModel.kt | 2 +- .../viewmodel/NetworkTimelineViewModel.kt | 28 +- .../timeline/viewmodel/TimelineViewModel.kt | 50 ++-- .../trending/TrendingTagViewHolder.kt | 5 +- .../trending/TrendingTagsFragment.kt | 6 +- .../viewmodel/TrendingTagsViewModel.kt | 11 +- .../ConversationLineItemDecoration.kt | 20 +- .../components/viewthread/ThreadAdapter.kt | 4 +- .../viewthread/ViewThreadFragment.kt | 34 ++- .../viewthread/ViewThreadViewModel.kt | 57 ++-- .../viewthread/edits/ViewEditsAdapter.kt | 19 +- .../viewthread/edits/ViewEditsFragment.kt | 25 +- .../viewthread/edits/ViewEditsViewModel.kt | 2 +- .../keylesspalace/tusky/db/AccountEntity.kt | 8 +- .../keylesspalace/tusky/db/AccountManager.kt | 5 +- .../com/keylesspalace/tusky/db/Converters.kt | 19 +- .../com/keylesspalace/tusky/db/DraftDao.kt | 4 +- .../com/keylesspalace/tusky/db/DraftEntity.kt | 4 +- .../com/keylesspalace/tusky/db/DraftsAlert.kt | 198 ++++++------ .../com/keylesspalace/tusky/db/TimelineDao.kt | 40 ++- .../tusky/db/TimelineStatusEntity.kt | 12 +- .../com/keylesspalace/tusky/di/AppInjector.kt | 6 +- .../com/keylesspalace/tusky/di/AppModule.kt | 2 +- .../tusky/di/CoroutineScopeModule.kt | 2 +- .../keylesspalace/tusky/di/NetworkModule.kt | 21 +- .../tusky/di/ViewModelFactory.kt | 13 +- .../keylesspalace/tusky/di/WorkerModule.kt | 8 +- .../com/keylesspalace/tusky/entity/Account.kt | 9 +- .../keylesspalace/tusky/entity/Attachment.kt | 9 +- .../tusky/entity/Conversation.kt | 3 +- .../com/keylesspalace/tusky/entity/Filter.kt | 2 +- .../keylesspalace/tusky/entity/Instance.kt | 16 +- .../keylesspalace/tusky/entity/MastoList.kt | 5 +- .../tusky/entity/Notification.kt | 7 +- .../com/keylesspalace/tusky/entity/Poll.kt | 3 +- .../tusky/entity/Relationship.kt | 6 +- .../com/keylesspalace/tusky/entity/Status.kt | 3 +- .../tusky/entity/TimelineAccount.kt | 6 +- .../keylesspalace/tusky/fragment/SFragment.kt | 36 ++- .../tusky/fragment/ViewImageFragment.kt | 12 +- .../tusky/fragment/ViewMediaFragment.kt | 12 +- .../tusky/fragment/ViewVideoFragment.kt | 16 +- .../tusky/json/GuardedBooleanAdapter.kt | 6 +- .../keylesspalace/tusky/json/Iso8601Utils.kt | 60 +++- .../network/InstanceSwitchAuthInterceptor.kt | 7 +- .../tusky/network/MastodonApi.kt | 197 ++++-------- .../tusky/network/ProgressRequestBody.kt | 4 +- .../tusky/pager/ImagePagerAdapter.kt | 3 +- .../tusky/pager/MainPagerAdapter.kt | 4 +- ...NotificationBlockStateBroadcastReceiver.kt | 11 +- .../receiver/SendStatusBroadcastReceiver.kt | 22 +- .../receiver/UnifiedPushBroadcastReceiver.kt | 6 +- .../tusky/service/SendStatusService.kt | 40 ++- .../settings/AccountPreferenceDataStore.kt | 2 +- .../tusky/settings/ProxyConfiguration.kt | 13 +- .../tusky/settings/SettingsDSL.kt | 5 +- .../tusky/usecase/TimelineCases.kt | 6 +- .../tusky/util/AbsoluteTimeFormatter.kt | 20 +- .../keylesspalace/tusky/util/CryptoUtil.kt | 6 +- .../tusky/util/CustomEmojiHelper.kt | 28 +- .../tusky/util/EmptyPagingSource.kt | 6 +- .../tusky/util/FlowExtensions.kt | 22 +- .../tusky/util/FocalPointUtil.kt | 7 +- .../tusky/util/HttpHeaderLink.kt | 5 +- .../com/keylesspalace/tusky/util/IOUtils.kt | 5 +- .../keylesspalace/tusky/util/LinkHelper.kt | 66 +++- .../util/ListStatusAccessibilityDelegate.kt | 12 +- .../keylesspalace/tusky/util/LocaleUtils.kt | 5 +- .../keylesspalace/tusky/util/MediaUtils.kt | 10 +- .../keylesspalace/tusky/util/PairedList.kt | 2 +- .../com/keylesspalace/tusky/util/Resource.kt | 6 +- .../tusky/util/ShareShortcutHelper.kt | 7 +- .../tusky/util/SmartLengthInputFilter.kt | 9 +- .../com/keylesspalace/tusky/util/SpanUtils.kt | 22 +- .../tusky/util/StatusDisplayOptions.kt | 10 +- .../tusky/util/StatusViewHelper.kt | 49 ++- .../tusky/util/ThrowableExtensions.kt | 2 +- .../tusky/util/ViewBindingExtensions.kt | 12 +- .../keylesspalace/tusky/util/ViewDataUtils.kt | 1 + .../tusky/view/ClickableSpanTextView.kt | 5 +- .../keylesspalace/tusky/view/LicenseCard.kt | 8 +- .../tusky/view/MediaPreviewImageView.kt | 15 +- .../tusky/view/SliderPreference.kt | 35 ++- .../tusky/viewdata/AttachmentViewData.kt | 5 +- .../tusky/viewdata/PollViewData.kt | 11 +- .../viewmodel/AccountsInListViewModel.kt | 7 +- .../tusky/viewmodel/EditProfileViewModel.kt | 31 +- .../tusky/viewmodel/ListsViewModel.kt | 23 +- .../tusky/worker/NotificationWorker.kt | 10 +- .../tusky/worker/PruneCacheWorker.kt | 10 +- .../tusky/BottomSheetActivityTest.kt | 4 +- .../com/keylesspalace/tusky/FilterV1Test.kt | 4 +- .../keylesspalace/tusky/MainActivityTest.kt | 2 +- .../components/compose/ComposeActivityTest.kt | 10 +- .../CachedTimelineRemoteMediatorTest.kt | 2 +- .../NetworkTimelineRemoteMediatorTest.kt | 8 +- .../viewthread/ViewThreadViewModelTest.kt | 2 +- .../tusky/usecase/TimelineCasesTest.kt | 2 +- .../tusky/util/AbsoluteTimeFormatterTest.kt | 4 +- .../tusky/util/FlowExtensionsTest.kt | 4 +- .../tusky/util/NumberUtilsTest.kt | 4 +- gradle/libs.versions.toml | 2 +- 215 files changed, 2813 insertions(+), 1177 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt diff --git a/.editorconfig b/.editorconfig index 499ac15ad..e435f4e01 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,12 +8,20 @@ insert_final_newline = true trim_trailing_whitespace = true [*.{java,kt}] +ij_kotlin_imports_layout = * + # Disable wildcard imports ij_kotlin_name_count_to_use_star_import = 999 ij_kotlin_name_count_to_use_star_import_for_members = 999 ij_java_class_count_to_use_import_on_demand = 999 -# Enable trailing comma -ktlint_disabled_rules=trailing-comma-on-call-site,trailing-comma-on-declaration-site + +ktlint_code_style = android_studio + +# Disable trailing comma +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled + +max_line_length = off [*.{yml,yaml}] indent_size = 2 diff --git a/app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt index b8b64692e..8f920a88b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt @@ -21,8 +21,8 @@ import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.util.NoUnderlineURLSpan import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class AboutActivity : BottomSheetActivity(), Injectable { @Inject @@ -70,9 +70,15 @@ class AboutActivity : BottomSheetActivity(), Injectable { binding.aboutPoweredByTusky.hide() } - binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_tusky_license) - binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site) - binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site) + binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines( + R.string.about_tusky_license + ) + binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines( + R.string.about_project_site + ) + binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines( + R.string.about_bug_feature_request_site + ) binding.tuskyProfileButton.setOnClickListener { viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt index a20526e95..0e88e0c7e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt @@ -45,8 +45,8 @@ import com.keylesspalace.tusky.util.unsafeLazy import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewmodel.AccountsInListViewModel import com.keylesspalace.tusky.viewmodel.State -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch private typealias AccountInfo = Pair @@ -82,11 +82,18 @@ class AccountsInListFragment : DialogFragment(), Injectable { super.onStart() dialog?.apply { // Stretch dialog to the window - window?.setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT) + window?.setLayout( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT + ) } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { return inflater.inflate(R.layout.fragment_accounts_in_list, container, false) } @@ -164,15 +171,27 @@ class AccountsInListFragment : DialogFragment(), Injectable { return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: TimelineAccount, newItem: TimelineAccount): Boolean { + override fun areContentsTheSame( + oldItem: TimelineAccount, + newItem: TimelineAccount + ): Boolean { return oldItem == newItem } } - inner class Adapter : ListAdapter>(AccountDiffer) { + inner class Adapter : ListAdapter>( + AccountDiffer + ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemFollowRequestBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemFollowRequestBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) val holder = BindingHolder(binding) binding.notificationTextView.hide() @@ -186,7 +205,10 @@ class AccountsInListFragment : DialogFragment(), Injectable { return holder } - override fun onBindViewHolder(holder: BindingHolder, position: Int) { + override fun onBindViewHolder( + holder: BindingHolder, + position: Int + ) { val account = getItem(position) holder.binding.displayNameTextView.text = account.name.emojify(account.emojis, holder.binding.displayNameTextView, animateEmojis) holder.binding.usernameTextView.text = account.username @@ -204,10 +226,19 @@ class AccountsInListFragment : DialogFragment(), Injectable { } } - inner class SearchAdapter : ListAdapter>(SearchDiffer) { + inner class SearchAdapter : ListAdapter>( + SearchDiffer + ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemFollowRequestBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemFollowRequestBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) val holder = BindingHolder(binding) binding.notificationTextView.hide() @@ -224,7 +255,10 @@ class AccountsInListFragment : DialogFragment(), Injectable { return holder } - override fun onBindViewHolder(holder: BindingHolder, position: Int) { + override fun onBindViewHolder( + holder: BindingHolder, + position: Int + ) { val (account, inAList) = getItem(position) holder.binding.displayNameTextView.text = account.name.emojify(account.emojis, holder.binding.displayNameTextView, animateEmojis) diff --git a/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt b/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt index e09bd4df8..ac5ae4c4d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt @@ -64,7 +64,10 @@ abstract class BottomSheetActivity : BaseActivity() { }) } - open fun viewUrl(url: String, lookupFallbackBehavior: PostLookupFallbackBehavior = PostLookupFallbackBehavior.OPEN_IN_BROWSER) { + open fun viewUrl( + url: String, + lookupFallbackBehavior: PostLookupFallbackBehavior = PostLookupFallbackBehavior.OPEN_IN_BROWSER + ) { if (!looksLikeMastodonUrl(url)) { openLink(url) return @@ -121,10 +124,17 @@ abstract class BottomSheetActivity : BaseActivity() { startActivityWithSlideInAnimation(intent) } - protected open fun performUrlFallbackAction(url: String, fallbackBehavior: PostLookupFallbackBehavior) { + protected open fun performUrlFallbackAction( + url: String, + fallbackBehavior: PostLookupFallbackBehavior + ) { when (fallbackBehavior) { PostLookupFallbackBehavior.OPEN_IN_BROWSER -> openLink(url) - PostLookupFallbackBehavior.DISPLAY_ERROR -> Toast.makeText(this, getString(R.string.post_lookup_error_format, url), Toast.LENGTH_SHORT).show() + PostLookupFallbackBehavior.DISPLAY_ERROR -> Toast.makeText( + this, + getString(R.string.post_lookup_error_format, url), + Toast.LENGTH_SHORT + ).show() } } diff --git a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt index 94c160f74..f447984ef 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt @@ -57,8 +57,8 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class EditProfileActivity : BaseActivity(), Injectable { @@ -126,9 +126,17 @@ class EditProfileActivity : BaseActivity(), Injectable { binding.fieldList.layoutManager = LinearLayoutManager(this) binding.fieldList.adapter = accountFieldEditAdapter - val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply { sizeDp = 12; colorInt = Color.WHITE } + val plusDrawable = IconicsDrawable(this, GoogleMaterial.Icon.gmd_add).apply { + sizeDp = 12 + colorInt = Color.WHITE + } - binding.addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds(plusDrawable, null, null, null) + binding.addFieldButton.setCompoundDrawablesRelativeWithIntrinsicBounds( + plusDrawable, + null, + null, + null + ) binding.addFieldButton.setOnClickListener { accountFieldEditAdapter.addField() @@ -162,7 +170,9 @@ class EditProfileActivity : BaseActivity(), Injectable { .placeholder(R.drawable.avatar_default) .transform( FitCenter(), - RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp)) + RoundedCorners( + resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp) + ) ) .into(binding.avatarPreview) } @@ -175,7 +185,11 @@ class EditProfileActivity : BaseActivity(), Injectable { } } is Error -> { - Snackbar.make(binding.avatarButton, R.string.error_generic, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.avatarButton, + R.string.error_generic, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { viewModel.obtainProfile() } @@ -188,7 +202,10 @@ class EditProfileActivity : BaseActivity(), Injectable { lifecycleScope.launch { viewModel.instanceData.collect { instanceInfo -> maxAccountFields = instanceInfo.maxFields - accountFieldEditAdapter.setFieldLimits(instanceInfo.maxFieldNameLength, instanceInfo.maxFieldValueLength) + accountFieldEditAdapter.setFieldLimits( + instanceInfo.maxFieldNameLength, + instanceInfo.maxFieldValueLength + ) binding.addFieldButton.isVisible = accountFieldEditAdapter.itemCount < maxAccountFields } @@ -318,7 +335,11 @@ class EditProfileActivity : BaseActivity(), Injectable { private fun onPickFailure(throwable: Throwable?) { Log.w("EditProfileActivity", "failed to pick media", throwable) - Snackbar.make(binding.avatarButton, R.string.error_media_upload_sending, Snackbar.LENGTH_LONG).show() + Snackbar.make( + binding.avatarButton, + R.string.error_media_upload_sending, + Snackbar.LENGTH_LONG + ).show() } private fun showUnsavedChangesDialog() = lifecycleScope.launch { diff --git a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt index 3f56c914a..44e073ec0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt @@ -54,8 +54,8 @@ import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADED import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADING import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch // TODO use the ListSelectionFragment (and/or its adapter or binding) here; but keep the LoadingState from here (?) @@ -273,7 +273,12 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector { } } - private fun onPickedDialogName(name: String, listId: String?, exclusive: Boolean, replyPolicy: String) { + private fun onPickedDialogName( + name: String, + listId: String?, + exclusive: Boolean, + replyPolicy: String + ) { if (listId == null) { viewModel.createNewList(name, exclusive, replyPolicy) } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt index c64322790..8df97d386 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -141,9 +141,9 @@ import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import de.c1710.filemojicompat_ui.helpers.EMOJI_PREFERENCE import io.reactivex.rxjava3.schedulers.Schedulers +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import javax.inject.Inject class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector, MenuProvider { @Inject @@ -199,7 +199,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje val notificationId = intent.getIntExtra(NOTIFICATION_ID, -1) if (notificationId != -1) { // opened from a notification action, cancel the notification - val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = getSystemService( + NOTIFICATION_SERVICE + ) as NotificationManager notificationManager.cancel(intent.getStringExtra(NOTIFICATION_TAG), notificationId) } @@ -253,7 +255,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje // user clicked a notification, show follow requests for type FOLLOW_REQUEST, // otherwise show notification tab if (intent.getStringExtra(NOTIFICATION_TYPE) == Notification.Type.FOLLOW_REQUEST.name) { - val intent = AccountListActivity.newIntent(this, AccountListActivity.Type.FOLLOW_REQUESTS) + val intent = AccountListActivity.newIntent( + this, + AccountListActivity.Type.FOLLOW_REQUESTS + ) startActivityWithSlideInAnimation(intent) } else { showNotificationTab = true @@ -293,8 +298,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje setupDrawer( savedInstanceState, addSearchButton = hideTopToolbar, - addTrendingTagsButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING_TAGS), - addTrendingStatusesButton = !accountManager.activeAccount!!.tabPreferences.hasTab(TRENDING_STATUSES), + addTrendingTagsButton = !accountManager.activeAccount!!.tabPreferences.hasTab( + TRENDING_TAGS + ), + addTrendingStatusesButton = !accountManager.activeAccount!!.tabPreferences.hasTab( + TRENDING_STATUSES + ) ) /* Fetch user info while we're doing other things. This has to be done after setting up the @@ -320,7 +329,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje refreshMainDrawerItems( addSearchButton = hideTopToolbar, addTrendingTagsButton = !event.newTabs.hasTab(TRENDING_TAGS), - addTrendingStatusesButton = !event.newTabs.hasTab(TRENDING_STATUSES), + addTrendingStatusesButton = !event.newTabs.hasTab(TRENDING_STATUSES) ) setupTabs(false) @@ -333,7 +342,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje directMessageTab?.let { if (event.accountId == activeAccount.accountId) { val hasDirectMessageNotification = - event.notifications.any { it.type == Notification.Type.MENTION && it.status?.visibility == Status.Visibility.DIRECT } + event.notifications.any { + it.type == Notification.Type.MENTION && it.status?.visibility == Status.Visibility.DIRECT + } if (hasDirectMessageNotification) { showDirectMessageBadge(true) @@ -427,7 +438,13 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje // If the main toolbar is hidden then there's no space in the top/bottomNav to show // the menu items as icons, so forceably disable them - if (!binding.mainToolbar.isVisible) menu.forEach { it.setShowAsAction(SHOW_AS_ACTION_NEVER) } + if (!binding.mainToolbar.isVisible) { + menu.forEach { + it.setShowAsAction( + SHOW_AS_ACTION_NEVER + ) + } + } } override fun onMenuItemSelected(item: MenuItem): Boolean { @@ -503,7 +520,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } private fun forwardToComposeActivity(intent: Intent) { - val composeOptions = IntentCompat.getParcelableExtra(intent, COMPOSE_OPTIONS, ComposeActivity.ComposeOptions::class.java) + val composeOptions = IntentCompat.getParcelableExtra( + intent, + COMPOSE_OPTIONS, + ComposeActivity.ComposeOptions::class.java + ) val composeIntent = if (composeOptions != null) { ComposeActivity.startIntent(this, composeOptions) @@ -523,7 +544,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje savedInstanceState: Bundle?, addSearchButton: Boolean, addTrendingTagsButton: Boolean, - addTrendingStatusesButton: Boolean, + addTrendingStatusesButton: Boolean ) { val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() } @@ -553,7 +574,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje header.currentProfileName.ellipsize = TextUtils.TruncateAt.END header.accountHeaderBackground.setColorFilter(getColor(R.color.headerBackgroundFilter)) - header.accountHeaderBackground.setBackgroundColor(MaterialColors.getColor(header, R.attr.colorBackgroundAccent)) + header.accountHeaderBackground.setBackgroundColor( + MaterialColors.getColor(header, R.attr.colorBackgroundAccent) + ) val animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false) DrawerImageLoader.init(object : AbstractDrawerImageLoader() { @@ -589,7 +612,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje refreshMainDrawerItems( addSearchButton = addSearchButton, addTrendingTagsButton = addTrendingTagsButton, - addTrendingStatusesButton = addTrendingStatusesButton, + addTrendingStatusesButton = addTrendingStatusesButton ) setSavedInstance(savedInstanceState) } @@ -598,7 +621,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje private fun refreshMainDrawerItems( addSearchButton: Boolean, addTrendingTagsButton: Boolean, - addTrendingStatusesButton: Boolean, + addTrendingStatusesButton: Boolean ) { binding.mainDrawer.apply { itemAdapter.clear() @@ -884,7 +907,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje supportActionBar?.title = tabs[position].title(this@MainActivity) binding.mainToolbar.setOnClickListener { - (tabAdapter.getFragment(activeTabLayout.selectedTabPosition) as? ReselectableFragment)?.onReselect() + ( + tabAdapter.getFragment( + activeTabLayout.selectedTabPosition + ) as? ReselectableFragment + )?.onReselect() } updateProfiles() @@ -915,7 +942,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } // open LoginActivity to add new account if (profile.identifier == DRAWER_ITEM_ADD_ACCOUNT) { - startActivityWithSlideInAnimation(LoginActivity.getIntent(this, LoginActivity.MODE_ADDITIONAL_LOGIN)) + startActivityWithSlideInAnimation( + LoginActivity.getIntent(this, LoginActivity.MODE_ADDITIONAL_LOGIN) + ) return false } // change Account @@ -986,10 +1015,18 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje loadDrawerAvatar(me.avatar, false) accountManager.updateActiveAccount(me) - NotificationHelper.createNotificationChannelsForAccount(accountManager.activeAccount!!, this) + NotificationHelper.createNotificationChannelsForAccount( + accountManager.activeAccount!!, + this + ) // Setup push notifications - showMigrationNoticeIfNecessary(this, binding.mainCoordinatorLayout, binding.composeButton, accountManager) + showMigrationNoticeIfNecessary( + this, + binding.mainCoordinatorLayout, + binding.composeButton, + accountManager + ) if (NotificationHelper.areNotificationsEnabled(this, accountManager)) { lifecycleScope.launch { enablePushNotificationsWithFallback(this@MainActivity, mastodonApi, accountManager) @@ -1024,7 +1061,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje Glide.with(this) .asDrawable() .load(avatarUrl) - .transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))) + .transform( + RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)) + ) .apply { if (showPlaceholder) placeholder(R.drawable.avatar_default) } @@ -1054,7 +1093,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje Glide.with(this) .asBitmap() .load(avatarUrl) - .transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))) + .transform( + RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)) + ) .apply { if (showPlaceholder) placeholder(R.drawable.avatar_default) } @@ -1101,7 +1142,12 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } private fun updateAnnouncementsBadge() { - binding.mainDrawer.updateBadge(DRAWER_ITEM_ANNOUNCEMENTS, StringHolder(if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString())) + binding.mainDrawer.updateBadge( + DRAWER_ITEM_ANNOUNCEMENTS, + StringHolder( + if (unreadAnnouncementsCount <= 0) null else unreadAnnouncementsCount.toString() + ) + ) } private fun updateProfiles() { @@ -1165,7 +1211,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje * Switches the active account to the accountId and takes the user to the correct place according to the notification they clicked */ @JvmStatic - fun openNotificationIntent(context: Context, tuskyAccountId: Long, type: Notification.Type): Intent { + fun openNotificationIntent( + context: Context, + tuskyAccountId: Long, + type: Notification.Type + ): Intent { return accountSwitchIntent(context, tuskyAccountId).apply { putExtra(NOTIFICATION_TYPE, type.name) } diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 5907e7df1..44aa5064f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -38,8 +38,8 @@ import com.keylesspalace.tusky.util.isHttpNotFound import com.keylesspalace.tusky.util.viewBinding import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { @@ -49,7 +49,9 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { @Inject lateinit var eventHub: EventHub - private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate) + private val binding: ActivityStatuslistBinding by viewBinding( + ActivityStatuslistBinding::inflate + ) private lateinit var kind: Kind private var hashtag: String? = null private var followTagItem: MenuItem? = null @@ -136,10 +138,18 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { followTagItem?.isVisible = false unfollowTagItem?.isVisible = true - Snackbar.make(binding.root, getString(R.string.following_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.following_hashtag_success_format, tag), + Snackbar.LENGTH_SHORT + ).show() }, { - Snackbar.make(binding.root, getString(R.string.error_following_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_following_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to follow #$tag", it) } ) @@ -158,10 +168,18 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { followTagItem?.isVisible = true unfollowTagItem?.isVisible = false - Snackbar.make(binding.root, getString(R.string.unfollowing_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.unfollowing_hashtag_success_format, tag), + Snackbar.LENGTH_SHORT + ).show() }, { - Snackbar.make(binding.root, getString(R.string.error_unfollowing_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_unfollowing_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to unfollow #$tag", it) } ) @@ -238,7 +256,12 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { expiresInSeconds = null ).fold( { filter -> - if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = hashedTag, wholeWord = true).isSuccess) { + if (mastodonApi.addFilterKeyword( + filterId = filter.id, + keyword = hashedTag, + wholeWord = true + ).isSuccess + ) { // must be requested again; otherwise does not contain the keyword (but server does) mutedFilter = mastodonApi.getFilter(filter.id).getOrNull() @@ -246,7 +269,11 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) filterCreateSuccess = true } else { - Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_muting_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to mute #$tag") } }, @@ -265,12 +292,20 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { filterCreateSuccess = true }, { throwable -> - Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_muting_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to mute #$tag", throwable) } ) } else { - Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_muting_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to mute #$tag", throwable) } } @@ -278,7 +313,11 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { if (filterCreateSuccess) { updateTagMuteState(true) - Snackbar.make(binding.root, getString(R.string.muting_hashtag_success_format, tag), Snackbar.LENGTH_LONG).apply { + Snackbar.make( + binding.root, + getString(R.string.muting_hashtag_success_format, tag), + Snackbar.LENGTH_LONG + ).apply { setAction(R.string.action_view_filter) { val intent = if (mutedFilter != null) { Intent(this@StatusListActivity, EditFilterActivity::class.java).apply { @@ -339,10 +378,18 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { mutedFilterV1 = null mutedFilter = null - Snackbar.make(binding.root, getString(R.string.unmuting_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.unmuting_hashtag_success_format, tag), + Snackbar.LENGTH_SHORT + ).show() }, { throwable -> - Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + getString(R.string.error_unmuting_hashtag_format, tag), + Snackbar.LENGTH_SHORT + ).show() Log.e(TAG, "Failed to unmute #$tag", throwable) } ) diff --git a/app/src/main/java/com/keylesspalace/tusky/TabData.kt b/app/src/main/java/com/keylesspalace/tusky/TabData.kt index e779dc472..12c0346cb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabData.kt @@ -104,7 +104,11 @@ fun createTabDataFromId(id: String, arguments: List = emptyList()): TabD id = TRENDING_STATUSES, text = R.string.title_public_trending_statuses, icon = R.drawable.ic_hot_24dp, - fragment = { TimelineFragment.newInstance(TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES) } + fragment = { + TimelineFragment.newInstance( + TimelineViewModel.Kind.PUBLIC_TRENDING_STATUSES + ) + } ) HASHTAG -> TabData( id = HASHTAG, @@ -112,13 +116,22 @@ fun createTabDataFromId(id: String, arguments: List = emptyList()): TabD icon = R.drawable.ic_hashtag, fragment = { args -> TimelineFragment.newHashtagInstance(args) }, arguments = arguments, - title = { context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) } } + title = { context -> + arguments.joinToString(separator = " ") { + context.getString(R.string.title_tag, it) + } + } ) LIST -> TabData( id = LIST, text = R.string.list, icon = R.drawable.ic_list, - fragment = { args -> TimelineFragment.newInstance(TimelineViewModel.Kind.LIST, args.getOrNull(0).orEmpty()) }, + fragment = { args -> + TimelineFragment.newInstance( + TimelineViewModel.Kind.LIST, + args.getOrNull(0).orEmpty() + ) + }, arguments = arguments, title = { arguments.getOrNull(1).orEmpty() } ) diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt index 92a2926c0..9b735bfb5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt @@ -47,10 +47,10 @@ import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.util.regex.Pattern import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, ItemInteractionListener, ListSelectionFragment.ListSelectionListener { @@ -72,9 +72,13 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It private var tabsChanged = false - private val selectedItemElevation by unsafeLazy { resources.getDimension(R.dimen.selected_drag_item_elevation) } + private val selectedItemElevation by unsafeLazy { + resources.getDimension(R.dimen.selected_drag_item_elevation) + } - private val hashtagRegex by unsafeLazy { Pattern.compile("([\\w_]*[\\p{Alpha}_][\\w_]*)", Pattern.CASE_INSENSITIVE) } + private val hashtagRegex by unsafeLazy { + Pattern.compile("([\\w_]*[\\p{Alpha}_][\\w_]*)", Pattern.CASE_INSENSITIVE) + } private val onFabDismissedCallback = object : OnBackPressedCallback(false) { override fun handleOnBackPressed() { @@ -99,14 +103,19 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT) binding.currentTabsRecyclerView.adapter = currentTabsAdapter binding.currentTabsRecyclerView.layoutManager = LinearLayoutManager(this) - binding.currentTabsRecyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) + binding.currentTabsRecyclerView.addItemDecoration( + DividerItemDecoration(this, LinearLayoutManager.VERTICAL) + ) addTabAdapter = TabAdapter(listOf(createTabDataFromId(DIRECT)), true, this) binding.addTabRecyclerView.adapter = addTabAdapter binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this) touchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { - override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { + override fun getMovementFlags( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ): Int { return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.END) } @@ -118,7 +127,11 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It return MIN_TAB_COUNT < currentTabs.size } - override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean { + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { val temp = currentTabs[viewHolder.bindingAdapterPosition] currentTabs[viewHolder.bindingAdapterPosition] = currentTabs[target.bindingAdapterPosition] currentTabs[target.bindingAdapterPosition] = temp @@ -138,7 +151,10 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It } } - override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + override fun clearView( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder + ) { super.clearView(recyclerView, viewHolder) viewHolder.itemView.elevation = 0f } diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt index da272db62..c24a9fcd9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt @@ -40,10 +40,10 @@ import de.c1710.filemojicompat_defaults.DefaultEmojiPackList import de.c1710.filemojicompat_ui.helpers.EmojiPackHelper import de.c1710.filemojicompat_ui.helpers.EmojiPreference import io.reactivex.rxjava3.plugins.RxJavaPlugins -import org.conscrypt.Conscrypt import java.security.Security import java.util.concurrent.TimeUnit import javax.inject.Inject +import org.conscrypt.Conscrypt class TuskyApplication : Application(), HasAndroidInjector { @Inject @@ -78,7 +78,10 @@ class TuskyApplication : Application(), HasAndroidInjector { AppInjector.init(this) // Migrate shared preference keys and defaults from version to version. - val oldVersion = sharedPreferences.getInt(PrefKeys.SCHEMA_VERSION, NEW_INSTALL_SCHEMA_VERSION) + val oldVersion = sharedPreferences.getInt( + PrefKeys.SCHEMA_VERSION, + NEW_INSTALL_SCHEMA_VERSION + ) if (oldVersion != SCHEMA_VERSION) { upgradeSharedPreferences(oldVersion, SCHEMA_VERSION) } diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt index 0f0930244..df0250994 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt @@ -74,7 +74,12 @@ import javax.inject.Inject typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit -class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.PhotoActionsListener, ViewVideoFragment.VideoActionsListener { +class ViewMediaActivity : + BaseActivity(), + HasAndroidInjector, + ViewImageFragment.PhotoActionsListener, + ViewVideoFragment.VideoActionsListener { + @Inject lateinit var androidInjector: DispatchingAndroidInjector @@ -103,7 +108,11 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. supportPostponeEnterTransition() // Gather the parameters. - attachments = IntentCompat.getParcelableArrayListExtra(intent, EXTRA_ATTACHMENTS, AttachmentViewData::class.java) + attachments = IntentCompat.getParcelableArrayListExtra( + intent, + EXTRA_ATTACHMENTS, + AttachmentViewData::class.java + ) val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0) // Adapter is actually of existential type PageAdapter & SharedElementsTransitionListener @@ -215,7 +224,11 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. private fun downloadMedia() { val url = imageUrl ?: attachments!![binding.viewPager.currentItem].attachment.url val filename = Uri.parse(url).lastPathSegment - Toast.makeText(applicationContext, resources.getString(R.string.download_image, filename), Toast.LENGTH_SHORT).show() + Toast.makeText( + applicationContext, + resources.getString(R.string.download_image, filename), + Toast.LENGTH_SHORT + ).show() val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val request = DownloadManager.Request(Uri.parse(url)) @@ -225,8 +238,13 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. private fun requestDownloadMedia() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _, grantResults -> - if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + requestPermissions( + arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) + ) { _, grantResults -> + if ( + grantResults.isNotEmpty() && + grantResults[0] == PackageManager.PERMISSION_GRANTED + ) { downloadMedia() } else { showErrorDialog( @@ -243,7 +261,9 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. private fun onOpenStatus() { val attach = attachments!![binding.viewPager.currentItem] - startActivityWithSlideInAnimation(ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl)) + startActivityWithSlideInAnimation( + ViewThreadActivity.startIntent(this, attach.statusId, attach.statusUrl) + ) } private fun copyLink() { @@ -276,7 +296,9 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. private fun shareFile(file: File, mimeType: String?) { ShareCompat.IntentBuilder(this) .setType(mimeType) - .addStream(FileProvider.getUriForFile(applicationContext, "$APPLICATION_ID.fileprovider", file)) + .addStream( + FileProvider.getUriForFile(applicationContext, "$APPLICATION_ID.fileprovider", file) + ) .setChooserTitle(R.string.send_media_to) .startChooser() } @@ -366,7 +388,11 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment. private const val TAG = "ViewMediaActivity" @JvmStatic - fun newIntent(context: Context?, attachments: List, index: Int): Intent { + fun newIntent( + context: Context?, + attachments: List, + index: Int + ): Intent { val intent = Intent(context, ViewMediaActivity::class.java) intent.putParcelableArrayListExtra(EXTRA_ATTACHMENTS, ArrayList(attachments)) intent.putExtra(EXTRA_ATTACHMENT_INDEX, index) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt index a57c0aba9..d6aa6016c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountFieldEditAdapter.kt @@ -62,8 +62,15 @@ class AccountFieldEditAdapter : RecyclerView.Adapter { - val binding = ItemEditFieldBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemEditFieldBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt index cdb81fe0a..e2f461201 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/AccountSelectionAdapter.kt @@ -28,7 +28,10 @@ import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.util.emojify import com.keylesspalace.tusky.util.loadAvatar -class AccountSelectionAdapter(context: Context) : ArrayAdapter(context, R.layout.item_autocomplete_account) { +class AccountSelectionAdapter(context: Context) : ArrayAdapter( + context, + R.layout.item_autocomplete_account +) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { val binding = if (convertView == null) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt index 51aa43f73..37a0b11b3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/EmojiAdapter.kt @@ -36,8 +36,15 @@ class EmojiAdapter( override fun getItemCount() = emojiList.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemEmojiButtonBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemEmojiButtonBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt index 2bbdf44e8..d4953514e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt @@ -47,16 +47,26 @@ class FollowRequestViewHolder( showBotOverlay: Boolean ) { val wrappedName = account.name.unicodeWrap() - val emojifiedName: CharSequence = wrappedName.emojify(account.emojis, itemView, animateEmojis) + val emojifiedName: CharSequence = wrappedName.emojify( + account.emojis, + itemView, + animateEmojis + ) binding.displayNameTextView.text = emojifiedName if (showHeader) { - val wholeMessage: String = itemView.context.getString(R.string.notification_follow_request_format, wrappedName) + val wholeMessage: String = itemView.context.getString( + R.string.notification_follow_request_format, + wrappedName + ) binding.notificationTextView.text = SpannableStringBuilder(wholeMessage).apply { setSpan(StyleSpan(Typeface.BOLD), 0, wrappedName.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) }.emojify(account.emojis, itemView, animateEmojis) } binding.notificationTextView.visible(showHeader) - val formattedUsername = itemView.context.getString(R.string.post_username_format, account.username) + val formattedUsername = itemView.context.getString( + R.string.post_username_format, + account.username + ) binding.usernameTextView.text = formattedUsername if (account.note.isEmpty()) { binding.accountNote.hide() @@ -67,7 +77,9 @@ class FollowRequestViewHolder( .emojify(account.emojis, binding.accountNote, animateEmojis) setClickableText(binding.accountNote, emojifiedNote, emptyList(), null, linkListener) } - val avatarRadius = binding.avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) + val avatarRadius = binding.avatar.context.resources.getDimensionPixelSize( + R.dimen.avatar_radius_48dp + ) loadAvatar(account.avatar, binding.avatar, avatarRadius, animateAvatar) binding.avatarBadge.visible(showBotOverlay && account.bot) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt index daf8381e9..a7803565c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/LocaleAdapter.kt @@ -26,7 +26,11 @@ import com.keylesspalace.tusky.util.getTuskyDisplayName import com.keylesspalace.tusky.util.modernLanguageCode import java.util.Locale -class LocaleAdapter(context: Context, resource: Int, locales: List) : ArrayAdapter(context, resource, locales) { +class LocaleAdapter(context: Context, resource: Int, locales: List) : ArrayAdapter( + context, + resource, + locales +) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { return (super.getView(position, convertView, parent) as TextView).apply { setTextColor(MaterialColors.getColor(this, android.R.attr.textColorTertiary)) diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt index 3e4e1dad9..aac3c8159 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt @@ -67,7 +67,10 @@ class PollAdapter : RecyclerView.Adapter>() { .map { pollOptions.indexOf(it) } } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { val binding = ItemPollBinding.inflate(LayoutInflater.from(parent.context), parent, false) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt index 6b59672d1..10c2f14f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt @@ -40,7 +40,11 @@ class PreviewPollOptionsAdapter : RecyclerView.Adapter() { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreviewViewHolder { - return PreviewViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_poll_preview_option, parent, false)) + return PreviewViewHolder( + LayoutInflater.from( + parent.context + ).inflate(R.layout.item_poll_preview_option, parent, false) + ) } override fun getItemCount() = options.size diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/ReportNotificationViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/ReportNotificationViewHolder.kt index db2f79a99..3eef2f833 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/ReportNotificationViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/ReportNotificationViewHolder.kt @@ -31,12 +31,25 @@ import com.keylesspalace.tusky.util.unicodeWrap import java.util.Date class ReportNotificationViewHolder( - private val binding: ItemReportNotificationBinding, + private val binding: ItemReportNotificationBinding ) : RecyclerView.ViewHolder(binding.root) { - fun setupWithReport(reporter: TimelineAccount, report: Report, animateAvatar: Boolean, animateEmojis: Boolean) { - val reporterName = reporter.name.unicodeWrap().emojify(reporter.emojis, itemView, animateEmojis) - val reporteeName = report.targetAccount.name.unicodeWrap().emojify(report.targetAccount.emojis, itemView, animateEmojis) + fun setupWithReport( + reporter: TimelineAccount, + report: Report, + animateAvatar: Boolean, + animateEmojis: Boolean + ) { + val reporterName = reporter.name.unicodeWrap().emojify( + reporter.emojis, + itemView, + animateEmojis + ) + val reporteeName = report.targetAccount.name.unicodeWrap().emojify( + report.targetAccount.emojis, + itemView, + animateEmojis + ) val icon = ContextCompat.getDrawable(itemView.context, R.drawable.ic_flag_24dp) binding.notificationTopText.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null) @@ -52,17 +65,22 @@ class ReportNotificationViewHolder( report.targetAccount.avatar, binding.notificationReporteeAvatar, itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp), - animateAvatar, + animateAvatar ) loadAvatar( reporter.avatar, binding.notificationReporterAvatar, itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_24dp), - animateAvatar, + animateAvatar ) } - fun setupActionListener(listener: NotificationActionListener, reporteeId: String, reporterId: String, reportId: String) { + fun setupActionListener( + listener: NotificationActionListener, + reporteeId: String, + reporterId: String, + reportId: String + ) { binding.notificationReporteeAvatar.setOnClickListener { val position = bindingAdapterPosition if (position != RecyclerView.NO_POSITION) { diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt index e3e4f27e8..7643aec3b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TabAdapter.kt @@ -56,7 +56,11 @@ class TabAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { val binding = if (small) { - ItemTabPreferenceSmallBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ItemTabPreferenceSmallBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) } else { ItemTabPreferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false) } diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt index b44a392bf..c6ef00e81 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/CacheUpdater.kt @@ -3,12 +3,12 @@ package com.keylesspalace.tusky.appstore import com.google.gson.Gson import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import javax.inject.Inject class CacheUpdater @Inject constructor( eventHub: EventHub, diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt index 102c91155..6ef67c4c1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt @@ -21,6 +21,9 @@ data class PollVoteEvent(val statusId: String, val poll: Poll) : Event data class DomainMuteEvent(val instance: String) : Event data class AnnouncementReadEvent(val announcementId: String) : Event data class FilterUpdatedEvent(val filterContext: List) : Event -data class NewNotificationsEvent(val accountId: String, val notifications: List) : Event +data class NewNotificationsEvent( + val accountId: String, + val notifications: List +) : Event data class ConversationsLoadingEvent(val accountId: String) : Event data class NotificationsLoadingEvent(val accountId: String) : Event diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/EventsHub.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/EventsHub.kt index 9fb4a3c82..7be722f3d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/EventsHub.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/EventsHub.kt @@ -2,10 +2,10 @@ package com.keylesspalace.tusky.appstore import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.subjects.PublishSubject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import javax.inject.Inject import javax.inject.Singleton +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow interface Event diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt index ee0f88f8d..0d21e3a79 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt @@ -267,9 +267,18 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide binding.accountFragmentViewPager.adapter = adapter binding.accountFragmentViewPager.offscreenPageLimit = 2 - val pageTitles = arrayOf(getString(R.string.title_posts), getString(R.string.title_posts_with_replies), getString(R.string.title_posts_pinned), getString(R.string.title_media)) + val pageTitles = + arrayOf( + getString(R.string.title_posts), + getString(R.string.title_posts_with_replies), + getString(R.string.title_posts_pinned), + getString(R.string.title_media) + ) - TabLayoutMediator(binding.accountTabLayout, binding.accountFragmentViewPager) { tab, position -> + TabLayoutMediator( + binding.accountTabLayout, + binding.accountFragmentViewPager + ) { tab, position -> tab.text = pageTitles[position] }.attach() @@ -301,7 +310,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide val right = insets.getInsets(systemBars()).right val bottom = insets.getInsets(systemBars()).bottom val left = insets.getInsets(systemBars()).left - binding.accountCoordinatorLayout.updatePadding(right = right, bottom = bottom, left = left) + binding.accountCoordinatorLayout.updatePadding( + right = right, + bottom = bottom, + left = left + ) WindowInsetsCompat.CONSUMED } @@ -318,7 +331,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide val appBarElevation = resources.getDimension(R.dimen.actionbar_elevation) - val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation) + val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay( + this, + appBarElevation + ) toolbarBackground.fillColor = ColorStateList.valueOf(Color.TRANSPARENT) binding.accountToolbar.background = toolbarBackground @@ -341,7 +357,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide binding.accountHeaderInfoContainer.background = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation) - val avatarBackground = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation).apply { + val avatarBackground = MaterialShapeDrawable.createWithElevationOverlay( + this, + appBarElevation + ).apply { fillColor = ColorStateList.valueOf(toolbarColor) elevation = appBarElevation shapeAppearanceModel = ShapeAppearanceModel.builder() @@ -381,11 +400,17 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide binding.accountAvatarImageView.visible(scaledAvatarSize > 0) - val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f) + val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost( + 1f + ) window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int - val evaluatedToolbarColor = argbEvaluator.evaluate(transparencyPercent, Color.TRANSPARENT, toolbarColor) as Int + val evaluatedToolbarColor = argbEvaluator.evaluate( + transparencyPercent, + Color.TRANSPARENT, + toolbarColor + ) as Int toolbarBackground.fillColor = ColorStateList.valueOf(evaluatedToolbarColor) @@ -407,7 +432,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide when (it) { is Success -> onAccountChanged(it.data) is Error -> { - Snackbar.make(binding.accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.accountCoordinatorLayout, + R.string.error_generic, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { viewModel.refresh() } .show() } @@ -421,7 +450,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide } if (it is Error) { - Snackbar.make(binding.accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.accountCoordinatorLayout, + R.string.error_generic, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { viewModel.refresh() } .show() } @@ -466,14 +499,22 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide val fullUsername = getFullUsername(loadedAccount) val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager clipboard.setPrimaryClip(ClipData.newPlainText(null, fullUsername)) - Snackbar.make(binding.root, getString(R.string.account_username_copied), Snackbar.LENGTH_SHORT) + Snackbar.make( + binding.root, + getString(R.string.account_username_copied), + Snackbar.LENGTH_SHORT + ) .show() } true } } - val emojifiedNote = account.note.parseAsMastodonHtml().emojify(account.emojis, binding.accountNoteTextView, animateEmojis) + val emojifiedNote = account.note.parseAsMastodonHtml().emojify( + account.emojis, + binding.accountNoteTextView, + animateEmojis + ) setClickableText(binding.accountNoteTextView, emojifiedNote, emptyList(), null, this) accountFieldAdapter.fields = account.fields.orEmpty() @@ -503,7 +544,13 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide val isLight = resources.getBoolean(R.bool.lightNavigationBar) if (loadedAccount?.bot == true) { - val badgeView = getBadge(getColor(R.color.tusky_grey_50), R.drawable.ic_bot_24dp, getString(R.string.profile_badge_bot_text), isLight) + val badgeView = + getBadge( + getColor(R.color.tusky_grey_50), + R.drawable.ic_bot_24dp, + getString(R.string.profile_badge_bot_text), + isLight + ) binding.accountBadgeContainer.addView(badgeView) } @@ -873,7 +920,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide } else { AlertDialog.Builder(this) .setMessage(getString(R.string.mute_domain_warning, instance)) - .setPositiveButton(getString(R.string.mute_domain_warning_dialog_ok)) { _, _ -> viewModel.blockDomain(instance) } + .setPositiveButton( + getString(R.string.mute_domain_warning_dialog_ok) + ) { _, _ -> viewModel.blockDomain(instance) } .setNegativeButton(android.R.string.cancel, null) .show() } @@ -966,7 +1015,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide sendIntent.action = Intent.ACTION_SEND sendIntent.putExtra(Intent.EXTRA_TEXT, url) sendIntent.type = "text/plain" - startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_account_link_to))) + startActivity( + Intent.createChooser( + sendIntent, + resources.getText(R.string.send_account_link_to) + ) + ) } return true } @@ -978,7 +1032,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide sendIntent.action = Intent.ACTION_SEND sendIntent.putExtra(Intent.EXTRA_TEXT, fullUsername) sendIntent.type = "text/plain" - startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_account_username_to))) + startActivity( + Intent.createChooser( + sendIntent, + resources.getText(R.string.send_account_username_to) + ) + ) } return true } @@ -1009,7 +1068,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide } R.id.action_report -> { loadedAccount?.let { loadedAccount -> - startActivity(ReportActivity.getIntent(this, viewModel.accountId, loadedAccount.username)) + startActivity( + ReportActivity.getIntent(this, viewModel.accountId, loadedAccount.username) + ) } return true } @@ -1047,7 +1108,12 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide // text color with maximum contrast val textColor = if (isLight) Color.BLACK else Color.WHITE // badge color with 50% transparency so it blends in with the theme background - val backgroundColor = Color.argb(128, Color.red(baseColor), Color.green(baseColor), Color.blue(baseColor)) + val backgroundColor = Color.argb( + 128, + Color.red(baseColor), + Color.green(baseColor), + Color.blue(baseColor) + ) // a color between the text color and the badge color val outlineColor = ColorUtils.blendARGB(textColor, baseColor, 0.7f) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountFieldAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountFieldAdapter.kt index 86acb8132..1d6562825 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountFieldAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountFieldAdapter.kt @@ -38,8 +38,15 @@ class AccountFieldAdapter( override fun getItemCount() = fields.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemAccountFieldBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemAccountFieldBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } @@ -51,11 +58,20 @@ class AccountFieldAdapter( val emojifiedName = field.name.emojify(emojis, nameTextView, animateEmojis) nameTextView.text = emojifiedName - val emojifiedValue = field.value.parseAsMastodonHtml().emojify(emojis, valueTextView, animateEmojis) + val emojifiedValue = field.value.parseAsMastodonHtml().emojify( + emojis, + valueTextView, + animateEmojis + ) setClickableText(valueTextView, emojifiedValue, emptyList(), null, linkListener) if (field.verifiedAt != null) { - valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_check_circle, 0) + valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds( + 0, + 0, + R.drawable.ic_check_circle, + 0 + ) } else { valueTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountPagerAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountPagerAdapter.kt index baeeea43f..f41448ebc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountPagerAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountPagerAdapter.kt @@ -33,7 +33,11 @@ class AccountPagerAdapter( override fun createFragment(position: Int): Fragment { return when (position) { 0 -> TimelineFragment.newInstance(TimelineViewModel.Kind.USER, accountId, false) - 1 -> TimelineFragment.newInstance(TimelineViewModel.Kind.USER_WITH_REPLIES, accountId, false) + 1 -> TimelineFragment.newInstance( + TimelineViewModel.Kind.USER_WITH_REPLIES, + accountId, + false + ) 2 -> TimelineFragment.newInstance(TimelineViewModel.Kind.USER_PINNED, accountId, false) 3 -> AccountMediaFragment.newInstance(accountId) else -> throw AssertionError("Page $position is out of AccountPagerAdapter bounds") diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountViewModel.kt index 2cd4171a3..0bd622b9e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountViewModel.kt @@ -20,10 +20,10 @@ import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.getDomain +import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import javax.inject.Inject class AccountViewModel @Inject constructor( private val mastodonApi: MastodonApi, @@ -97,7 +97,15 @@ class AccountViewModel @Inject constructor( mastodonApi.relationships(listOf(accountId)) .fold( { relationships -> - relationshipData.postValue(if (relationships.isNotEmpty()) Success(relationships[0]) else Error()) + relationshipData.postValue( + if (relationships.isNotEmpty()) { + Success( + relationships[0] + ) + } else { + Error() + } + ) }, { t -> Log.w(TAG, "failed obtaining relationships", t) @@ -135,8 +143,8 @@ class AccountViewModel @Inject constructor( fun changeSubscribingState() { val relationship = relationshipData.value?.data - if (relationship?.notifying == true || /* Mastodon 3.3.0rc1 */ - relationship?.subscribing == true /* Pleroma */ + if (relationship?.notifying == true || // Mastodon 3.3.0rc1 + relationship?.subscribing == true // Pleroma ) { changeRelationship(RelationShipAction.UNSUBSCRIBE) } else { @@ -315,7 +323,14 @@ class AccountViewModel @Inject constructor( } enum class RelationShipAction { - FOLLOW, UNFOLLOW, BLOCK, UNBLOCK, MUTE, UNMUTE, SUBSCRIBE, UNSUBSCRIBE + FOLLOW, + UNFOLLOW, + BLOCK, + UNBLOCK, + MUTE, + UNMUTE, + SUBSCRIBE, + UNSUBSCRIBE } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListSelectionFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListSelectionFragment.kt index 7871878ce..585c7e311 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListSelectionFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListSelectionFragment.kt @@ -42,12 +42,12 @@ import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.visible +import javax.inject.Inject import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class ListSelectionFragment : DialogFragment(), Injectable { @@ -133,14 +133,22 @@ class ListSelectionFragment : DialogFragment(), Injectable { viewModel.actionError.collectLatest { error -> when (error.type) { ActionError.Type.ADD -> { - Snackbar.make(binding.root, R.string.failed_to_add_to_list, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.root, + R.string.failed_to_add_to_list, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { viewModel.addAccountToList(accountId!!, error.listId) } .show() } ActionError.Type.REMOVE -> { - Snackbar.make(binding.root, R.string.failed_to_remove_from_list, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.root, + R.string.failed_to_remove_from_list, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { viewModel.removeAccountFromList(accountId!!, error.listId) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountViewModel.kt index ccb57e1c8..5113b9fe0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/list/ListsForAccountViewModel.kt @@ -24,12 +24,12 @@ import at.connyduck.calladapter.networkresult.onSuccess import at.connyduck.calladapter.networkresult.runCatching import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.network.MastodonApi +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import javax.inject.Inject data class AccountListState( val list: MastoList, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt index 5a81c418e..49f19390a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt @@ -49,9 +49,9 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject /** * Fragment with multiple columns of media previews for the specified account. @@ -92,9 +92,13 @@ class AccountMediaFragment : ) val columnCount = view.context.resources.getInteger(R.integer.profile_media_column_count) - val imageSpacing = view.context.resources.getDimensionPixelSize(R.dimen.profile_media_spacing) + val imageSpacing = view.context.resources.getDimensionPixelSize( + R.dimen.profile_media_spacing + ) - binding.recyclerView.addItemDecoration(GridSpacingItemDecoration(columnCount, imageSpacing, 0)) + binding.recyclerView.addItemDecoration( + GridSpacingItemDecoration(columnCount, imageSpacing, 0) + ) binding.recyclerView.layoutManager = GridLayoutManager(view.context, columnCount) binding.recyclerView.adapter = adapter @@ -124,7 +128,11 @@ class AccountMediaFragment : is LoadState.NotLoading -> { if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) { binding.statusView.show() - binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null) + binding.statusView.setup( + R.drawable.elephant_friend_empty, + R.string.message_empty, + null + ) } } is LoadState.Error -> { @@ -175,11 +183,19 @@ class AccountMediaFragment : Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.AUDIO -> { - val intent = ViewMediaActivity.newIntent(context, attachmentsFromSameStatus, currentIndex) + val intent = ViewMediaActivity.newIntent( + context, + attachmentsFromSameStatus, + currentIndex + ) if (activity != null) { val url = selected.attachment.url ViewCompat.setTransitionName(view, url) - val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, url) + val options = ActivityOptionsCompat.makeSceneTransitionAnimation( + requireActivity(), + view, + url + ) startActivity(intent, options.toBundle()) } else { startActivity(intent) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaGridAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaGridAdapter.kt index aecbeb0bb..23901177f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaGridAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaGridAdapter.kt @@ -29,25 +29,48 @@ class AccountMediaGridAdapter( private val onAttachmentClickListener: (AttachmentViewData, View) -> Unit ) : PagingDataAdapter>( object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: AttachmentViewData, newItem: AttachmentViewData): Boolean { + override fun areItemsTheSame( + oldItem: AttachmentViewData, + newItem: AttachmentViewData + ): Boolean { return oldItem.attachment.id == newItem.attachment.id } - override fun areContentsTheSame(oldItem: AttachmentViewData, newItem: AttachmentViewData): Boolean { + override fun areContentsTheSame( + oldItem: AttachmentViewData, + newItem: AttachmentViewData + ): Boolean { return oldItem == newItem } } ) { - private val baseItemBackgroundColor = MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurface, Color.BLACK) - private val videoIndicator = AppCompatResources.getDrawable(context, R.drawable.ic_play_indicator) - private val mediaHiddenDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_hide_media_24dp) + private val baseItemBackgroundColor = MaterialColors.getColor( + context, + com.google.android.material.R.attr.colorSurface, + Color.BLACK + ) + private val videoIndicator = AppCompatResources.getDrawable( + context, + R.drawable.ic_play_indicator + ) + private val mediaHiddenDrawable = AppCompatResources.getDrawable( + context, + R.drawable.ic_hide_media_24dp + ) private val itemBgBaseHSV = FloatArray(3) private val random = Random() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemAccountMediaBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemAccountMediaBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) Color.colorToHSV(baseItemBackgroundColor, itemBgBaseHSV) itemBgBaseHSV[2] = itemBgBaseHSV[2] + random.nextFloat() / 3f - 1f / 6f binding.root.setBackgroundColor(Color.HSVToColor(itemBgBaseHSV)) @@ -71,7 +94,11 @@ class AccountMediaGridAdapter( if (item.attachment.type == Attachment.Type.AUDIO) { overlay.hide() - imageView.setPadding(context.resources.getDimensionPixelSize(R.dimen.profile_media_audio_icon_padding)) + imageView.setPadding( + context.resources.getDimensionPixelSize( + R.dimen.profile_media_audio_icon_padding + ) + ) Glide.with(imageView) .load(R.drawable.ic_music_box_preview_24dp) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt index e08429aad..6df9b387f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt @@ -59,9 +59,9 @@ import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.view.EndlessOnScrollListener import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import javax.inject.Inject import kotlinx.coroutines.launch import retrofit2.Response -import javax.inject.Inject class AccountListFragment : Fragment(R.layout.fragment_account_list), @@ -96,7 +96,9 @@ class AccountListFragment : val layoutManager = LinearLayoutManager(view.context) binding.recyclerView.layoutManager = layoutManager (binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false - binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) + binding.recyclerView.addItemDecoration( + DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL) + ) binding.swipeRefreshLayout.setOnRefreshListener { fetchAccounts() } binding.swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue) @@ -116,7 +118,8 @@ class AccountListFragment : instanceName = activeAccount.domain, accountLocked = activeAccount.locked ) - val followRequestsAdapter = FollowRequestsAdapter(this, this, animateAvatar, animateEmojis, showBotOverlay) + val followRequestsAdapter = + FollowRequestsAdapter(this, this, animateAvatar, animateEmojis, showBotOverlay) binding.recyclerView.adapter = ConcatAdapter(headerAdapter, followRequestsAdapter) followRequestsAdapter } @@ -142,7 +145,9 @@ class AccountListFragment : override fun onViewTag(tag: String) { (activity as BaseActivity?) - ?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) + ?.startActivityWithSlideInAnimation( + StatusListActivity.newHashtagIntent(requireContext(), tag) + ) } override fun onViewAccount(id: String) { @@ -225,7 +230,11 @@ class AccountListFragment : val unblockedUser = blocksAdapter.removeItem(position) if (unblockedUser != null) { - Snackbar.make(binding.recyclerView, R.string.confirmation_unblocked, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.recyclerView, + R.string.confirmation_unblocked, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_undo) { blocksAdapter.addItem(unblockedUser, position) onBlock(true, id, position) @@ -243,11 +252,7 @@ class AccountListFragment : Log.e(TAG, "Failed to $verb account accountId $accountId") } - override fun onRespondToFollowRequest( - accept: Boolean, - accountId: String, - position: Int - ) { + override fun onRespondToFollowRequest(accept: Boolean, accountId: String, position: Int) { if (accept) { api.authorizeFollowRequest(accountId) } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/AccountAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/AccountAdapter.kt index 7d050e7e4..1a9a7513c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/AccountAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/AccountAdapter.kt @@ -60,9 +60,7 @@ abstract class AccountAdapter internal constructo } } - private fun createFooterViewHolder( - parent: ViewGroup - ): RecyclerView.ViewHolder { + private fun createFooterViewHolder(parent: ViewGroup): RecyclerView.ViewHolder { val binding = ItemFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/BlocksAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/BlocksAdapter.kt index 2ef520d5e..c1132e7f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/BlocksAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/BlocksAdapter.kt @@ -39,16 +39,27 @@ class BlocksAdapter( ) { override fun createAccountViewHolder(parent: ViewGroup): BindingHolder { - val binding = ItemBlockedUserBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ItemBlockedUserBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } - override fun onBindAccountViewHolder(viewHolder: BindingHolder, position: Int) { + override fun onBindAccountViewHolder( + viewHolder: BindingHolder, + position: Int + ) { val account = accountList[position] val binding = viewHolder.binding val context = binding.root.context - val emojifiedName = account.name.emojify(account.emojis, binding.blockedUserDisplayName, animateEmojis) + val emojifiedName = account.name.emojify( + account.emojis, + binding.blockedUserDisplayName, + animateEmojis + ) binding.blockedUserDisplayName.text = emojifiedName val formattedUsername = context.getString(R.string.post_username_format, account.username) binding.blockedUserUsername.text = formattedUsername diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/FollowRequestsHeaderAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/FollowRequestsHeaderAdapter.kt index 85cf4e20a..069e799e1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/FollowRequestsHeaderAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/FollowRequestsHeaderAdapter.kt @@ -27,12 +27,22 @@ class FollowRequestsHeaderAdapter( private val accountLocked: Boolean ) : RecyclerView.Adapter>() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemFollowRequestsHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemFollowRequestsHeaderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } - override fun onBindViewHolder(viewHolder: BindingHolder, position: Int) { + override fun onBindViewHolder( + viewHolder: BindingHolder, + position: Int + ) { viewHolder.binding.root.text = viewHolder.binding.root.context.getString(R.string.follow_requests_info, instanceName) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/MutesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/MutesAdapter.kt index 288d13394..d685730de 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/MutesAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/accountlist/adapter/MutesAdapter.kt @@ -42,18 +42,29 @@ class MutesAdapter( private val mutingNotificationsMap = HashMap() override fun createAccountViewHolder(parent: ViewGroup): BindingHolder { - val binding = ItemMutedUserBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ItemMutedUserBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } - override fun onBindAccountViewHolder(viewHolder: BindingHolder, position: Int) { + override fun onBindAccountViewHolder( + viewHolder: BindingHolder, + position: Int + ) { val account = accountList[position] val binding = viewHolder.binding val context = binding.root.context val mutingNotifications = mutingNotificationsMap[account.id] - val emojifiedName = account.name.emojify(account.emojis, binding.mutedUserDisplayName, animateEmojis) + val emojifiedName = account.name.emojify( + account.emojis, + binding.mutedUserDisplayName, + animateEmojis + ) binding.mutedUserDisplayName.text = emojifiedName val formattedUsername = context.getString(R.string.post_username_format, account.username) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt index 4c903ee81..4e835dbcb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementAdapter.kt @@ -54,8 +54,15 @@ class AnnouncementAdapter( private val absoluteTimeFormatter = AbsoluteTimeFormatter() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemAnnouncementBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemAnnouncementBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } @@ -69,7 +76,11 @@ class AnnouncementAdapter( val chips = holder.binding.chipGroup val addReactionChip = holder.binding.addReactionChip - val emojifiedText: CharSequence = item.content.parseAsMastodonHtml().emojify(item.emojis, text, animateEmojis) + val emojifiedText: CharSequence = item.content.parseAsMastodonHtml().emojify( + item.emojis, + text, + animateEmojis + ) setClickableText(text, emojifiedText, item.mentions, item.tags, listener) @@ -107,7 +118,13 @@ class AnnouncementAdapter( spanBuilder.setSpan(span, 0, 1, 0) Glide.with(this) .asDrawable() - .load(if (animateEmojis) { reaction.url } else { reaction.staticUrl }) + .load( + if (animateEmojis) { + reaction.url + } else { + reaction.staticUrl + } + ) .into(span.getTarget(animateEmojis)) this.text = spanBuilder } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt index cc503a7c8..13526fe21 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt @@ -116,7 +116,10 @@ class AnnouncementsActivity : binding.progressBar.hide() binding.swipeRefreshLayout.isRefreshing = false if (it.data.isNullOrEmpty()) { - binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_announcements) + binding.errorMessageView.setup( + R.drawable.elephant_friend_empty, + R.string.no_announcements + ) binding.errorMessageView.show() } else { binding.errorMessageView.hide() @@ -129,7 +132,10 @@ class AnnouncementsActivity : is Error -> { binding.progressBar.hide() binding.swipeRefreshLayout.isRefreshing = false - binding.errorMessageView.setup(R.drawable.errorphant_error, R.string.error_generic) { + binding.errorMessageView.setup( + R.drawable.errorphant_error, + R.string.error_generic + ) { refreshAnnouncements() } binding.errorMessageView.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsViewModel.kt index 8abad91ac..9fad312eb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsViewModel.kt @@ -31,8 +31,8 @@ import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Success -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class AnnouncementsViewModel @Inject constructor( private val instanceInfoRepo: InstanceInfoRepository, @@ -64,7 +64,9 @@ class AnnouncementsViewModel @Inject constructor( mastodonApi.dismissAnnouncement(announcement.id) .fold( { - eventHub.dispatch(AnnouncementReadEvent(announcement.id)) + eventHub.dispatch( + AnnouncementReadEvent(announcement.id) + ) }, { throwable -> Log.d( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index 0ee91e48f..07bd2d2ae 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -115,11 +115,6 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.parcelize.Parcelize import java.io.File import java.io.IOException import java.text.DecimalFormat @@ -127,6 +122,11 @@ import java.util.Locale import javax.inject.Inject import kotlin.math.max import kotlin.math.min +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize class ComposeActivity : BaseActivity(), @@ -163,14 +163,23 @@ class ComposeActivity : private var maxUploadMediaNumber = InstanceInfoRepository.DEFAULT_MAX_MEDIA_ATTACHMENTS - private val takePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) { success -> - if (success) { - pickMedia(photoUploadUri!!) + private val takePicture = + registerForActivityResult(ActivityResultContracts.TakePicture()) { success -> + if (success) { + pickMedia(photoUploadUri!!) + } } - } private val pickMediaFile = registerForActivityResult(PickMediaFiles()) { uris -> if (viewModel.media.value.size + uris.size > maxUploadMediaNumber) { - Toast.makeText(this, resources.getQuantityString(R.plurals.error_upload_max_media_reached, maxUploadMediaNumber, maxUploadMediaNumber), Toast.LENGTH_SHORT).show() + Toast.makeText( + this, + resources.getQuantityString( + R.plurals.error_upload_max_media_reached, + maxUploadMediaNumber, + maxUploadMediaNumber + ), + Toast.LENGTH_SHORT + ).show() } else { uris.forEach { uri -> pickMedia(uri) @@ -191,7 +200,8 @@ class ComposeActivity : uriNew, size, itemOld.description, - null, // Intentionally reset focus when cropping + // Intentionally reset focus when cropping + null, itemOld ) } @@ -222,7 +232,11 @@ class ComposeActivity : val mediaAdapter = MediaPreviewAdapter( this, onAddCaption = { item -> - CaptionDialog.newInstance(item.localId, item.description, item.uri).show(supportFragmentManager, "caption_dialog") + CaptionDialog.newInstance( + item.localId, + item.description, + item.uri + ).show(supportFragmentManager, "caption_dialog") }, onAddFocus = { item -> makeFocusDialog(item.focus, item.uri) { newFocus -> @@ -240,7 +254,11 @@ class ComposeActivity : /* If the composer is started up as a reply to another post, override the "starting" state * based on what the intent from the reply request passes. */ - val composeOptions: ComposeOptions? = IntentCompat.getParcelableExtra(intent, COMPOSE_OPTIONS_EXTRA, ComposeOptions::class.java) + val composeOptions: ComposeOptions? = IntentCompat.getParcelableExtra( + intent, + COMPOSE_OPTIONS_EXTRA, + ComposeOptions::class.java + ) viewModel.setup(composeOptions) setupButtons() @@ -303,12 +321,20 @@ class ComposeActivity : if (type.startsWith("image/") || type.startsWith("video/") || type.startsWith("audio/")) { when (intent.action) { Intent.ACTION_SEND -> { - IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)?.let { uri -> + IntentCompat.getParcelableExtra( + intent, + Intent.EXTRA_STREAM, + Uri::class.java + )?.let { uri -> pickMedia(uri) } } Intent.ACTION_SEND_MULTIPLE -> { - IntentCompat.getParcelableArrayListExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)?.forEach { uri -> + IntentCompat.getParcelableArrayListExtra( + intent, + Intent.EXTRA_STREAM, + Uri::class.java + )?.forEach { uri -> pickMedia(uri) } } @@ -328,7 +354,13 @@ class ComposeActivity : val end = binding.composeEditField.selectionEnd.coerceAtLeast(0) val left = min(start, end) val right = max(start, end) - binding.composeEditField.text.replace(left, right, shareBody, 0, shareBody.length) + binding.composeEditField.text.replace( + left, + right, + shareBody, + 0, + shareBody.length + ) // move edittext cursor to first when shareBody parsed binding.composeEditField.text.insert(0, "\n") binding.composeEditField.setSelection(0) @@ -341,23 +373,48 @@ class ComposeActivity : if (replyingStatusAuthor != null) { binding.composeReplyView.show() binding.composeReplyView.text = getString(R.string.replying_to, replyingStatusAuthor) - val arrowDownIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_down).apply { sizeDp = 12 } + val arrowDownIcon = IconicsDrawable( + this, + GoogleMaterial.Icon.gmd_arrow_drop_down + ).apply { + sizeDp = 12 + } setDrawableTint(this, arrowDownIcon, android.R.attr.textColorTertiary) - binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) + binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds( + null, + null, + arrowDownIcon, + null + ) binding.composeReplyView.setOnClickListener { - TransitionManager.beginDelayedTransition(binding.composeReplyContentView.parent as ViewGroup) + TransitionManager.beginDelayedTransition( + binding.composeReplyContentView.parent as ViewGroup + ) if (binding.composeReplyContentView.isVisible) { binding.composeReplyContentView.hide() - binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowDownIcon, null) + binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds( + null, + null, + arrowDownIcon, + null + ) } else { binding.composeReplyContentView.show() - val arrowUpIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_arrow_drop_up).apply { sizeDp = 12 } + val arrowUpIcon = IconicsDrawable( + this, + GoogleMaterial.Icon.gmd_arrow_drop_up + ).apply { sizeDp = 12 } setDrawableTint(this, arrowUpIcon, android.R.attr.textColorTertiary) - binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrowUpIcon, null) + binding.composeReplyView.setCompoundDrawablesRelativeWithIntrinsicBounds( + null, + null, + arrowUpIcon, + null + ) } } } @@ -374,7 +431,12 @@ class ComposeActivity : private fun setupComposeField(preferences: SharedPreferences, startingText: String?) { binding.composeEditField.setOnReceiveContentListener(this) - binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) } + binding.composeEditField.setOnKeyListener { _, keyCode, event -> + this.onKeyDown( + keyCode, + event + ) + } binding.composeEditField.setAdapter( ComposeAutoCompleteAdapter( @@ -419,7 +481,9 @@ class ComposeActivity : } lifecycleScope.launch { - viewModel.showContentWarning.combine(viewModel.markMediaAsSensitive) { showContentWarning, markSensitive -> + viewModel.showContentWarning.combine( + viewModel.markMediaAsSensitive + ) { showContentWarning, markSensitive -> updateSensitiveMediaToggle(markSensitive, showContentWarning) showContentWarning(showContentWarning) }.collect() @@ -434,7 +498,10 @@ class ComposeActivity : mediaAdapter.submitList(media) binding.composeMediaPreviewBar.visible(media.isNotEmpty()) - updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value, viewModel.showContentWarning.value) + updateSensitiveMediaToggle( + viewModel.markMediaAsSensitive.value, + viewModel.showContentWarning.value + ) } } @@ -510,16 +577,42 @@ class ComposeActivity : val textColor = MaterialColors.getColor(binding.root, android.R.attr.textColorTertiary) - val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { colorInt = textColor; sizeDp = 18 } - binding.actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds(cameraIcon, null, null, null) + val cameraIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_camera_alt).apply { + colorInt = textColor + sizeDp = 18 + } + binding.actionPhotoTake.setCompoundDrawablesRelativeWithIntrinsicBounds( + cameraIcon, + null, + null, + null + ) - val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply { colorInt = textColor; sizeDp = 18 } - binding.actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds(imageIcon, null, null, null) + val imageIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_image).apply { + colorInt = textColor + sizeDp = 18 + } + binding.actionPhotoPick.setCompoundDrawablesRelativeWithIntrinsicBounds( + imageIcon, + null, + null, + null + ) - val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply { colorInt = textColor; sizeDp = 18 } - binding.addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(pollIcon, null, null, null) + val pollIcon = IconicsDrawable(this, GoogleMaterial.Icon.gmd_poll).apply { + colorInt = textColor + sizeDp = 18 + } + binding.addPollTextActionTextView.setCompoundDrawablesRelativeWithIntrinsicBounds( + pollIcon, + null, + null, + null + ) - binding.actionPhotoTake.visible(Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(packageManager) != null) + binding.actionPhotoTake.visible( + Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(packageManager) != null + ) binding.actionPhotoTake.setOnClickListener { initiateCameraApp() } binding.actionPhotoPick.setOnClickListener { onMediaPick() } @@ -549,7 +642,12 @@ class ComposeActivity : private fun setupLanguageSpinner(initialLanguages: List) { binding.composePostLanguageButton.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { + override fun onItemSelected( + parent: AdapterView<*>, + view: View?, + position: Int, + id: Long + ) { viewModel.postLanguage = (parent.adapter.getItem(position) as Locale).modernLanguageCode } @@ -594,8 +692,12 @@ class ComposeActivity : private fun replaceTextAtCaret(text: CharSequence) { // If you select "backward" in an editable, you get SelectionStart > SelectionEnd - val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd) - val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd) + val start = binding.composeEditField.selectionStart.coerceAtMost( + binding.composeEditField.selectionEnd + ) + val end = binding.composeEditField.selectionStart.coerceAtLeast( + binding.composeEditField.selectionEnd + ) val textToInsert = if (start > 0 && !binding.composeEditField.text[start - 1].isWhitespace()) { " $text" } else { @@ -609,8 +711,12 @@ class ComposeActivity : fun prependSelectedWordsWith(text: CharSequence) { // If you select "backward" in an editable, you get SelectionStart > SelectionEnd - val start = binding.composeEditField.selectionStart.coerceAtMost(binding.composeEditField.selectionEnd) - val end = binding.composeEditField.selectionStart.coerceAtLeast(binding.composeEditField.selectionEnd) + val start = binding.composeEditField.selectionStart.coerceAtMost( + binding.composeEditField.selectionEnd + ) + val end = binding.composeEditField.selectionStart.coerceAtLeast( + binding.composeEditField.selectionEnd + ) val editorText = binding.composeEditField.text if (start == end) { @@ -678,7 +784,10 @@ class ComposeActivity : this.viewModel.toggleMarkSensitive() } - private fun updateSensitiveMediaToggle(markMediaSensitive: Boolean, contentWarningShown: Boolean) { + private fun updateSensitiveMediaToggle( + markMediaSensitive: Boolean, + contentWarningShown: Boolean + ) { if (viewModel.media.value.isEmpty()) { binding.composeHideMediaButton.hide() binding.descriptionMissingWarningButton.hide() @@ -695,7 +804,10 @@ class ComposeActivity : getColor(R.color.tusky_blue) } else { binding.composeHideMediaButton.setImageResource(R.drawable.ic_eye_24dp) - MaterialColors.getColor(binding.composeHideMediaButton, android.R.attr.textColorTertiary) + MaterialColors.getColor( + binding.composeHideMediaButton, + android.R.attr.textColorTertiary + ) } } binding.composeHideMediaButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN) @@ -717,7 +829,10 @@ class ComposeActivity : enableButton(binding.composeScheduleButton, clickable = false, colorActive = false) } else { @ColorInt val color = if (binding.composeScheduleView.time == null) { - MaterialColors.getColor(binding.composeScheduleButton, android.R.attr.textColorTertiary) + MaterialColors.getColor( + binding.composeScheduleButton, + android.R.attr.textColorTertiary + ) } else { getColor(R.color.tusky_blue) } @@ -748,7 +863,11 @@ class ComposeActivity : binding.composeToggleVisibilityButton.setImageResource(iconRes) if (viewModel.editing) { // Can't update visibility on published status - enableButton(binding.composeToggleVisibilityButton, clickable = false, colorActive = false) + enableButton( + binding.composeToggleVisibilityButton, + clickable = false, + colorActive = false + ) } } @@ -785,7 +904,11 @@ class ComposeActivity : private fun showEmojis() { binding.emojiView.adapter?.let { if (it.itemCount == 0) { - val errorMessage = getString(R.string.error_no_custom_emojis, accountManager.activeAccount!!.domain) + val errorMessage = + getString( + R.string.error_no_custom_emojis, + accountManager.activeAccount!!.domain + ) displayTransientMessage(errorMessage) } else { if (emojiBehavior.state == BottomSheetBehavior.STATE_HIDDEN || emojiBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { @@ -852,9 +975,14 @@ class ComposeActivity : private fun setupPollView() { val margin = resources.getDimensionPixelSize(R.dimen.compose_media_preview_margin) - val marginBottom = resources.getDimensionPixelSize(R.dimen.compose_media_preview_margin_bottom) + val marginBottom = resources.getDimensionPixelSize( + R.dimen.compose_media_preview_margin_bottom + ) - val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + val layoutParams = LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) layoutParams.setMargins(margin, margin, margin, marginBottom) binding.pollPreview.layoutParams = layoutParams @@ -905,7 +1033,10 @@ class ComposeActivity : val textColor = if (remainingLength < 0) { getColor(R.color.tusky_red) } else { - MaterialColors.getColor(binding.composeCharactersLeftView, android.R.attr.textColorTertiary) + MaterialColors.getColor( + binding.composeCharactersLeftView, + android.R.attr.textColorTertiary + ) } binding.composeCharactersLeftView.setTextColor(textColor) } @@ -917,7 +1048,9 @@ class ComposeActivity : } private fun verifyScheduledTime(): Boolean { - return binding.composeScheduleView.verifyScheduledTime(binding.composeScheduleView.getDateTime(viewModel.scheduledAt.value)) + return binding.composeScheduleView.verifyScheduledTime( + binding.composeScheduleView.getDateTime(viewModel.scheduledAt.value) + ) } private fun onSendClicked() { @@ -967,7 +1100,11 @@ class ComposeActivity : } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) { @@ -1042,14 +1179,20 @@ class ComposeActivity : val tempFile = createNewImageFile(this, if (isPng) ".png" else ".jpg") // "Authority" must be the same as the android:authorities string in AndroidManifest.xml - val uriNew = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", tempFile) + val uriNew = FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID + ".fileprovider", + tempFile + ) viewModel.cropImageItemOld = item cropImage.launch( options(uri = item.uri) { setOutputUri(uriNew) - setOutputCompressFormat(if (isPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG) + setOutputCompressFormat( + if (isPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG + ) } ) } @@ -1087,7 +1230,9 @@ class ComposeActivity : val formattedSize = decimalFormat.format(allowedSizeInMb) getString(R.string.error_multimedia_size_limit, formattedSize) } - is VideoOrImageException -> getString(R.string.error_media_upload_image_or_video) + is VideoOrImageException -> getString( + R.string.error_media_upload_image_or_video + ) else -> getString(R.string.error_media_upload_opening) } displayTransientMessage(errorString) @@ -1096,16 +1241,23 @@ class ComposeActivity : } private fun showContentWarning(show: Boolean) { - TransitionManager.beginDelayedTransition(binding.composeContentWarningBar.parent as ViewGroup) + TransitionManager.beginDelayedTransition( + binding.composeContentWarningBar.parent as ViewGroup + ) @ColorInt val color = if (show) { binding.composeContentWarningBar.show() - binding.composeContentWarningField.setSelection(binding.composeContentWarningField.text.length) + binding.composeContentWarningField.setSelection( + binding.composeContentWarningField.text.length + ) binding.composeContentWarningField.requestFocus() getColor(R.color.tusky_blue) } else { binding.composeContentWarningBar.hide() binding.composeEditField.requestFocus() - MaterialColors.getColor(binding.composeContentWarningButton, android.R.attr.textColorTertiary) + MaterialColors.getColor( + binding.composeContentWarningButton, + android.R.attr.textColorTertiary + ) } binding.composeContentWarningButton.drawable.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN) } @@ -1159,7 +1311,10 @@ class ComposeActivity : /** * User is editing a new post, and can either save the changes as a draft or discard them. */ - private fun getSaveAsDraftOrDiscardDialog(contentText: String, contentWarning: String): AlertDialog.Builder { + private fun getSaveAsDraftOrDiscardDialog( + contentText: String, + contentWarning: String + ): AlertDialog.Builder { val warning = if (viewModel.media.value.isNotEmpty()) { R.string.compose_save_draft_loses_media } else { @@ -1182,7 +1337,10 @@ class ComposeActivity : * User is editing an existing draft, and can either update the draft with the new changes or * discard them. */ - private fun getUpdateDraftOrDiscardDialog(contentText: String, contentWarning: String): AlertDialog.Builder { + private fun getUpdateDraftOrDiscardDialog( + contentText: String, + contentWarning: String + ): AlertDialog.Builder { val warning = if (viewModel.media.value.isNotEmpty()) { R.string.compose_save_draft_loses_media } else { @@ -1286,10 +1444,15 @@ class ComposeActivity : val state: State ) { enum class Type { - IMAGE, VIDEO, AUDIO; + IMAGE, + VIDEO, + AUDIO } enum class State { - UPLOADING, UNPROCESSED, PROCESSED, PUBLISHED + UPLOADING, + UNPROCESSED, + PROCESSED, + PUBLISHED } } @@ -1370,10 +1533,7 @@ class ComposeActivity : * @return an Intent to start the ComposeActivity */ @JvmStatic - fun startIntent( - context: Context, - options: ComposeOptions - ): Intent { + fun startIntent(context: Context, options: ComposeOptions): Intent { return Intent(context, ComposeActivity::class.java).apply { putExtra(COMPOSE_OPTIONS_EXTRA, options) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeAutoCompleteAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeAutoCompleteAdapter.kt index e825798cf..16c1eabda 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeAutoCompleteAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeAutoCompleteAdapter.kt @@ -108,7 +108,9 @@ class ComposeAutoCompleteAdapter( val account = accountResult.account binding.username.text = context.getString(R.string.post_username_format, account.username) binding.displayName.text = account.name.emojify(account.emojis, binding.displayName, animateEmojis) - val avatarRadius = context.resources.getDimensionPixelSize(R.dimen.avatar_radius_42dp) + val avatarRadius = context.resources.getDimensionPixelSize( + R.dimen.avatar_radius_42dp + ) loadAvatar( account.avatar, binding.avatar, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt index 78cf25f54..50bd3fdfc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt @@ -38,6 +38,7 @@ import com.keylesspalace.tusky.service.MediaToSend import com.keylesspalace.tusky.service.ServiceClient import com.keylesspalace.tusky.service.StatusToSend import com.keylesspalace.tusky.util.randomAlphanumericString +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -50,7 +51,6 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject class ComposeViewModel @Inject constructor( private val api: MastodonApi, @@ -85,13 +85,19 @@ class ComposeViewModel @Inject constructor( val markMediaAsSensitive: MutableStateFlow = MutableStateFlow(accountManager.activeAccount?.defaultMediaSensitivity ?: false) - val statusVisibility: MutableStateFlow = MutableStateFlow(Status.Visibility.UNKNOWN) + val statusVisibility: MutableStateFlow = + MutableStateFlow(Status.Visibility.UNKNOWN) val showContentWarning: MutableStateFlow = MutableStateFlow(false) val poll: MutableStateFlow = MutableStateFlow(null) val scheduledAt: MutableStateFlow = MutableStateFlow(null) val media: MutableStateFlow> = MutableStateFlow(emptyList()) - val uploadError = MutableSharedFlow(replay = 0, extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + val uploadError = + MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private lateinit var composeKind: ComposeKind @@ -100,7 +106,13 @@ class ComposeViewModel @Inject constructor( private var setupComplete = false - suspend fun pickMedia(mediaUri: Uri, description: String? = null, focus: Attachment.Focus? = null): Result = withContext(Dispatchers.IO) { + suspend fun pickMedia( + mediaUri: Uri, + description: String? = null, + focus: Attachment.Focus? = null + ): Result = withContext( + Dispatchers.IO + ) { try { val (type, uri, size) = mediaUploader.prepareMedia(mediaUri, instanceInfo.first()) val mediaItems = media.value @@ -164,7 +176,11 @@ class ComposeViewModel @Inject constructor( item.copy( id = event.mediaId, uploadPercent = -1, - state = if (event.processed) { QueuedMedia.State.PROCESSED } else { QueuedMedia.State.UNPROCESSED } + state = if (event.processed) { + QueuedMedia.State.PROCESSED + } else { + QueuedMedia.State.UNPROCESSED + } ) is UploadEvent.ErrorEvent -> { media.update { mediaList -> mediaList.filter { it.localId != mediaItem.localId } } @@ -186,7 +202,13 @@ class ComposeViewModel @Inject constructor( return mediaItem } - private fun addUploadedMedia(id: String, type: QueuedMedia.Type, uri: Uri, description: String?, focus: Attachment.Focus?) { + private fun addUploadedMedia( + id: String, + type: QueuedMedia.Type, + uri: Uri, + description: String?, + focus: Attachment.Focus? + ) { media.update { mediaList -> val mediaItem = QueuedMedia( localId = mediaUploader.getNewLocalMediaId(), @@ -305,11 +327,7 @@ class ComposeViewModel @Inject constructor( * Send status to the server. * Uses current state plus provided arguments. */ - suspend fun sendStatus( - content: String, - spoilerText: String, - accountId: Long - ) { + suspend fun sendStatus(content: String, spoilerText: String, accountId: Long) { if (!scheduledTootId.isNullOrEmpty()) { api.deleteScheduledStatus(scheduledTootId!!) } @@ -382,7 +400,11 @@ class ComposeViewModel @Inject constructor( }) } '#' -> { - return api.searchSync(query = token, type = SearchType.Hashtag.apiParameter, limit = 10) + return api.searchSync( + query = token, + type = SearchType.Hashtag.apiParameter, + limit = 10 + ) .fold({ searchResult -> searchResult.hashtags.map { AutocompleteResult.HashtagResult(it.name) } }, { e -> diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ImageDownsizer.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ImageDownsizer.kt index 39b444688..c45db990a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ImageDownsizer.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ImageDownsizer.kt @@ -54,10 +54,10 @@ fun downsizeImage( // Get EXIF data, for orientation info. val orientation = getImageOrientation(uri, contentResolver) /* Unfortunately, there isn't a determined worst case compression ratio for image - * formats. So, the only way to tell if they're too big is to compress them and - * test, and keep trying at smaller sizes. The initial estimate should be good for - * many cases, so it should only iterate once, but the loop is used to be absolutely - * sure it gets downsized to below the limit. */ + * formats. So, the only way to tell if they're too big is to compress them and + * test, and keep trying at smaller sizes. The initial estimate should be good for + * many cases, so it should only iterate once, but the loop is used to be absolutely + * sure it gets downsized to below the limit. */ var scaledImageSize = 1024 do { val outputStream = try { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaPreviewAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaPreviewAdapter.kt index 6cd590d7e..bfc814293 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaPreviewAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaPreviewAdapter.kt @@ -113,11 +113,17 @@ class MediaPreviewAdapter( private val differ = AsyncListDiffer( this, object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: ComposeActivity.QueuedMedia, newItem: ComposeActivity.QueuedMedia): Boolean { + override fun areItemsTheSame( + oldItem: ComposeActivity.QueuedMedia, + newItem: ComposeActivity.QueuedMedia + ): Boolean { return oldItem.localId == newItem.localId } - override fun areContentsTheSame(oldItem: ComposeActivity.QueuedMedia, newItem: ComposeActivity.QueuedMedia): Boolean { + override fun areContentsTheSame( + oldItem: ComposeActivity.QueuedMedia, + newItem: ComposeActivity.QueuedMedia + ): Boolean { return oldItem == newItem } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt index caab8401e..3ec43748a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt @@ -37,6 +37,13 @@ import com.keylesspalace.tusky.util.getImageSquarePixels import com.keylesspalace.tusky.util.getMediaSize import com.keylesspalace.tusky.util.getServerErrorMessage import com.keylesspalace.tusky.util.randomAlphanumericString +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.IOException +import java.util.Date +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -54,19 +61,15 @@ import kotlinx.coroutines.flow.shareIn import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import retrofit2.HttpException -import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.io.IOException -import java.util.Date -import javax.inject.Inject -import javax.inject.Singleton sealed interface FinalUploadEvent sealed class UploadEvent { data class ProgressEvent(val percentage: Int) : UploadEvent() - data class FinishedEvent(val mediaId: String, val processed: Boolean) : UploadEvent(), FinalUploadEvent + data class FinishedEvent( + val mediaId: String, + val processed: Boolean + ) : UploadEvent(), FinalUploadEvent data class ErrorEvent(val error: Throwable) : UploadEvent(), FinalUploadEvent } @@ -80,11 +83,7 @@ fun createNewImageFile(context: Context, suffix: String = ".jpg"): File { val randomId = randomAlphanumericString(12) val imageFileName = "Tusky_${randomId}_" val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) - return File.createTempFile( - imageFileName, /* prefix */ - suffix, /* suffix */ - storageDir /* directory */ - ) + return File.createTempFile(imageFileName, suffix, storageDir) } data class PreparedMedia(val type: QueuedMedia.Type, val uri: Uri, val size: Long) @@ -256,9 +255,9 @@ class MediaUploader @Inject constructor( // .m4a files. See https://github.com/tuskyapp/Tusky/issues/3189 for details. // Sniff the content of the file to determine the actual type. if (mimeType != null && ( - mimeType.startsWith("audio/", ignoreCase = true) || - mimeType.startsWith("video/", ignoreCase = true) - ) + mimeType.startsWith("audio/", ignoreCase = true) || + mimeType.startsWith("video/", ignoreCase = true) + ) ) { val retriever = MediaMetadataRetriever() retriever.setDataSource(context, media.uri) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt index 2d76e2d03..1d8168db6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollDialog.kt @@ -60,7 +60,9 @@ fun showAddPollDialog( binding.pollChoices.adapter = adapter var durations = context.resources.getIntArray(R.array.poll_duration_values).toList() - val durationLabels = context.resources.getStringArray(R.array.poll_duration_names).filterIndexed { index, _ -> durations[index] in minDuration..maxDuration } + val durationLabels = context.resources.getStringArray( + R.array.poll_duration_names + ).filterIndexed { index, _ -> durations[index] in minDuration..maxDuration } binding.pollDurationSpinner.adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, durationLabels).apply { setDropDownViewResource(androidx.appcompat.R.layout.support_simple_spinner_dropdown_item) } @@ -75,8 +77,8 @@ fun showAddPollDialog( } } - val DAY_SECONDS = 60 * 60 * 24 - val desiredDuration = poll?.expiresIn ?: DAY_SECONDS + val secondsInADay = 60 * 60 * 24 + val desiredDuration = poll?.expiresIn ?: secondsInADay val pollDurationId = durations.indexOfLast { it <= desiredDuration } @@ -105,5 +107,7 @@ fun showAddPollDialog( dialog.show() // make the dialog focusable so the keyboard does not stay behind it - dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) + dialog.window?.clearFlags( + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM + ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt index 4d7ecdca8..ad7f7db18 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/AddPollOptionsAdapter.kt @@ -41,8 +41,15 @@ class AddPollOptionsAdapter( notifyItemInserted(options.size - 1) } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemAddPollOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemAddPollOptionBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) val holder = BindingHolder(binding) binding.optionEditText.filters = arrayOf(InputFilter.LengthFilter(maxOptionLength)) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt index 79f232614..2edffbf15 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/CaptionDialog.kt @@ -133,17 +133,14 @@ class CaptionDialog : DialogFragment() { } companion object { - fun newInstance( - localId: Int, - existingDescription: String?, - previewUri: Uri - ) = CaptionDialog().apply { - arguments = bundleOf( - LOCAL_ID_ARG to localId, - EXISTING_DESCRIPTION_ARG to existingDescription, - PREVIEW_URI_ARG to previewUri - ) - } + fun newInstance(localId: Int, existingDescription: String?, previewUri: Uri) = + CaptionDialog().apply { + arguments = bundleOf( + LOCAL_ID_ARG to localId, + EXISTING_DESCRIPTION_ARG to existingDescription, + PREVIEW_URI_ARG to previewUri + ) + } private const val DESCRIPTION_KEY = "description" private const val EXISTING_DESCRIPTION_ARG = "existing_description" diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/FocusDialog.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/FocusDialog.kt index e6abea84e..6cfbeedfc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/FocusDialog.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/dialog/FocusDialog.kt @@ -49,11 +49,22 @@ fun T.makeFocusDialog( .load(previewUri) .downsample(DownsampleStrategy.CENTER_INSIDE) .listener(object : RequestListener { - override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target, p3: Boolean): Boolean { + override fun onLoadFailed( + p0: GlideException?, + p1: Any?, + p2: Target, + p3: Boolean + ): Boolean { return false } - override fun onResourceReady(resource: Drawable, model: Any, target: Target?, dataSource: DataSource, isFirstResource: Boolean): Boolean { + override fun onResourceReady( + resource: Drawable, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { val width = resource.intrinsicWidth val height = resource.intrinsicHeight diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt index 02ec9a9bc..e576bfef2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt @@ -21,7 +21,10 @@ import android.widget.RadioGroup import com.keylesspalace.tusky.R import com.keylesspalace.tusky.entity.Status -class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : RadioGroup(context, attrs) { +class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : RadioGroup( + context, + attrs +) { var listener: ComposeOptionsListener? = null diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.kt index 2f6fad162..a68906d9e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.kt @@ -223,7 +223,8 @@ class ComposeScheduleView } companion object { - var MINIMUM_SCHEDULED_SECONDS = 330 // Minimum is 5 minutes, pad 30 seconds for posting + // Minimum is 5 minutes, pad 30 seconds for posting + private const val MINIMUM_SCHEDULED_SECONDS = 330 fun calendar(): Calendar = Calendar.getInstance(TimeZone.getDefault()) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/FocusIndicatorView.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/FocusIndicatorView.kt index 6e0b83dc5..7cda8cc21 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/FocusIndicatorView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/FocusIndicatorView.kt @@ -68,7 +68,9 @@ class FocusIndicatorView return offset.toFloat() + ((value + 1.0f) / 2.0f) * innerLimit.toFloat() // From range -1..1 } - @SuppressLint("ClickableViewAccessibility") // Android Studio wants us to implement PerformClick for accessibility, but that unfortunately cannot be made meaningful for this widget. + @SuppressLint( + "ClickableViewAccessibility" + ) // Android Studio wants us to implement PerformClick for accessibility, but that unfortunately cannot be made meaningful for this widget. override fun onTouchEvent(event: MotionEvent): Boolean { if (event.actionMasked == MotionEvent.ACTION_CANCEL) { return false @@ -112,7 +114,13 @@ class FocusIndicatorView curtainPath.reset() // Draw a flood fill with a hole cut out of it curtainPath.fillType = Path.FillType.WINDING - curtainPath.addRect(0.0f, 0.0f, this.width.toFloat(), this.height.toFloat(), Path.Direction.CW) + curtainPath.addRect( + 0.0f, + 0.0f, + this.width.toFloat(), + this.height.toFloat(), + Path.Direction.CW + ) curtainPath.addCircle(x, y, circleRadius, Path.Direction.CCW) canvas.drawPath(curtainPath, curtainPaint) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt index 43e8f6ef9..995e400e1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/TootButton.kt @@ -60,7 +60,10 @@ class TootButton Status.Visibility.PRIVATE, Status.Visibility.DIRECT -> { setText(R.string.action_send) - IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply { sizeDp = 18; colorInt = Color.WHITE } + IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply { + sizeDp = 18 + colorInt = Color.WHITE + } } else -> { null diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt index a5a8ed27d..beca7342d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationAdapter.kt @@ -38,7 +38,9 @@ class ConversationAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConversationViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_conversation, parent, false) + val view = LayoutInflater.from( + parent.context + ).inflate(R.layout.item_conversation, parent, false) return ConversationViewHolder(view, statusDisplayOptions, listener) } @@ -58,15 +60,24 @@ class ConversationAdapter( companion object { val CONVERSATION_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: ConversationViewData, newItem: ConversationViewData): Boolean { + override fun areItemsTheSame( + oldItem: ConversationViewData, + newItem: ConversationViewData + ): Boolean { return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: ConversationViewData, newItem: ConversationViewData): Boolean { + override fun areContentsTheSame( + oldItem: ConversationViewData, + newItem: ConversationViewData + ): Boolean { return false // Items are different always. It allows to refresh timestamp on every view holder update } - override fun getChangePayload(oldItem: ConversationViewData, newItem: ConversationViewData): Any? { + override fun getChangePayload( + oldItem: ConversationViewData, + newItem: ConversationViewData + ): Any? { return if (oldItem == newItem) { // If items are equal - update timestamp only listOf(StatusBaseViewHolder.Key.KEY_CREATED) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt index f830b5bd9..438f3eeba 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt @@ -140,21 +140,16 @@ data class ConversationStatusEntity( } } -fun TimelineAccount.toEntity() = - ConversationAccountEntity( - id = id, - localUsername = localUsername, - username = username, - displayName = name, - avatar = avatar, - emojis = emojis.orEmpty() - ) +fun TimelineAccount.toEntity() = ConversationAccountEntity( + id = id, + localUsername = localUsername, + username = username, + displayName = name, + avatar = avatar, + emojis = emojis.orEmpty() +) -fun Status.toEntity( - expanded: Boolean, - contentShowing: Boolean, - contentCollapsed: Boolean -) = +fun Status.toEntity(expanded: Boolean, contentShowing: Boolean, contentCollapsed: Boolean) = ConversationStatusEntity( id = id, url = url, @@ -188,16 +183,15 @@ fun Conversation.toEntity( expanded: Boolean, contentShowing: Boolean, contentCollapsed: Boolean -) = - ConversationEntity( - accountId = accountId, - id = id, - order = order, - accounts = accounts.map { it.toEntity() }, - unread = unread, - lastStatus = lastStatus!!.toEntity( - expanded = expanded, - contentShowing = contentShowing, - contentCollapsed = contentCollapsed - ) +) = ConversationEntity( + accountId = accountId, + id = id, + order = order, + accounts = accounts.map { it.toEntity() }, + unread = unread, + lastStatus = lastStatus!!.toEntity( + expanded = expanded, + contentShowing = contentShowing, + contentCollapsed = contentCollapsed ) +) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationLoadStateAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationLoadStateAdapter.kt index 7ff4daa74..b8373f986 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationLoadStateAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationLoadStateAdapter.kt @@ -27,7 +27,10 @@ class ConversationLoadStateAdapter( private val retryCallback: () -> Unit ) : LoadStateAdapter>() { - override fun onBindViewHolder(holder: BindingHolder, loadState: LoadState) { + override fun onBindViewHolder( + holder: BindingHolder, + loadState: LoadState + ) { val binding = holder.binding binding.progressBar.visible(loadState == LoadState.Loading) binding.retryButton.visible(loadState is LoadState.Error) @@ -47,7 +50,11 @@ class ConversationLoadStateAdapter( parent: ViewGroup, loadState: LoadState ): BindingHolder { - val binding = ItemNetworkStateBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ItemNetworkStateBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt index 3e7eb421e..1dff9e654 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt @@ -63,12 +63,12 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.time.DurationUnit import kotlin.time.toDuration +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch class ConversationsFragment : SFragment(), @@ -91,7 +91,11 @@ class ConversationsFragment : private var hideFab = false - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { return inflater.inflate(R.layout.fragment_timeline, container, false) } @@ -141,13 +145,19 @@ class ConversationsFragment : is LoadState.NotLoading -> { if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) { binding.statusView.show() - binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null) + binding.statusView.setup( + R.drawable.elephant_friend_empty, + R.string.message_empty, + null + ) binding.statusView.showHelp(R.string.help_empty_conversations) } } is LoadState.Error -> { binding.statusView.show() - binding.statusView.setup((loadState.refresh as LoadState.Error).error) { refreshContent() } + binding.statusView.setup( + (loadState.refresh as LoadState.Error).error + ) { refreshContent() } } is LoadState.Loading -> { binding.progressBar.show() @@ -240,7 +250,9 @@ class ConversationsFragment : binding.recyclerView.setHasFixedSize(true) binding.recyclerView.layoutManager = LinearLayoutManager(context) - binding.recyclerView.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + binding.recyclerView.addItemDecoration( + DividerItemDecoration(context, DividerItemDecoration.VERTICAL) + ) (binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false @@ -298,7 +310,11 @@ class ConversationsFragment : override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) { adapter.peek(position)?.let { conversation -> - viewMedia(attachmentIndex, AttachmentViewData.list(conversation.lastStatus.status), view) + viewMedia( + attachmentIndex, + AttachmentViewData.list(conversation.lastStatus.status), + view + ) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsRemoteMediator.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsRemoteMediator.kt index b00c99a95..cf81e5ef6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsRemoteMediator.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsRemoteMediator.kt @@ -38,7 +38,10 @@ class ConversationsRemoteMediator( } try { - val conversationsResponse = api.getConversations(maxId = nextKey, limit = state.config.pageSize) + val conversationsResponse = api.getConversations( + maxId = nextKey, + limit = state.config.pageSize + ) val conversations = conversationsResponse.body() if (!conversationsResponse.isSuccessful || conversations == null) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt index 78bb7c8e5..c4cd2a6f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt @@ -29,9 +29,9 @@ import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.usecase.TimelineCases import com.keylesspalace.tusky.util.EmptyPagingSource +import javax.inject.Inject import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import javax.inject.Inject class ConversationsViewModel @Inject constructor( private val timelineCases: TimelineCases, @@ -91,7 +91,11 @@ class ConversationsViewModel @Inject constructor( fun voteInPoll(choices: List, conversation: ConversationViewData) { viewModelScope.launch { - timelineCases.voteInPoll(conversation.lastStatus.id, conversation.lastStatus.status.poll?.id!!, choices) + timelineCases.voteInPoll( + conversation.lastStatus.id, + conversation.lastStatus.status.poll?.id!!, + choices + ) .fold({ poll -> val newConversation = conversation.toEntity( accountId = accountManager.activeAccount!!.id, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksAdapter.kt index e37aa917c..29d42aafe 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksAdapter.kt @@ -11,8 +11,15 @@ class DomainBlocksAdapter( private val onUnmute: (String) -> Unit ) : PagingDataAdapter>(STRING_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemBlockedDomainBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemBlockedDomainBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt index 896e81ead..1adfb911c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt @@ -18,9 +18,9 @@ import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class DomainBlocksFragment : Fragment(R.layout.fragment_domain_blocks), Injectable { @@ -35,7 +35,9 @@ class DomainBlocksFragment : Fragment(R.layout.fragment_domain_blocks), Injectab val adapter = DomainBlocksAdapter(viewModel::unblock) binding.recyclerView.setHasFixedSize(true) - binding.recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) + binding.recyclerView.addItemDecoration( + DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL) + ) binding.recyclerView.adapter = adapter binding.recyclerView.layoutManager = LinearLayoutManager(view.context) @@ -52,7 +54,9 @@ class DomainBlocksFragment : Fragment(R.layout.fragment_domain_blocks), Injectab } adapter.addLoadStateListener { loadState -> - binding.progressBar.visible(loadState.refresh == LoadState.Loading && adapter.itemCount == 0) + binding.progressBar.visible( + loadState.refresh == LoadState.Loading && adapter.itemCount == 0 + ) if (loadState.refresh is LoadState.Error) { binding.recyclerView.hide() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksViewModel.kt index 6458977f0..04f4f426d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksViewModel.kt @@ -8,9 +8,9 @@ import androidx.paging.cachedIn import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.onFailure import com.keylesspalace.tusky.R +import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.launch -import javax.inject.Inject class DomainBlocksViewModel @Inject constructor( private val repo: DomainBlocksRepository diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt index 2dc802e6a..68a7dec68 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt @@ -29,18 +29,18 @@ import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.util.copyToFile -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient -import okhttp3.Request -import okio.buffer -import okio.sink import java.io.File import java.io.IOException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.Request +import okio.buffer +import okio.sink class DraftHelper @Inject constructor( val context: Context, @@ -200,6 +200,10 @@ class DraftHelper @Inject constructor( } else { this.copyToFile(contentResolver, file) } - return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file) + return FileProvider.getUriForFile( + context, + BuildConfig.APPLICATION_ID + ".fileprovider", + file + ) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftMediaAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftMediaAdapter.kt index 2165f7e0c..fd6816b7b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftMediaAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftMediaAdapter.kt @@ -35,7 +35,10 @@ class DraftMediaAdapter( return oldItem == newItem } - override fun areContentsTheSame(oldItem: DraftAttachment, newItem: DraftAttachment): Boolean { + override fun areContentsTheSame( + oldItem: DraftAttachment, + newItem: DraftAttachment + ): Boolean { return oldItem == newItem } } @@ -75,7 +78,9 @@ class DraftMediaAdapter( RecyclerView.ViewHolder(imageView) { init { val thumbnailViewSize = - imageView.context.resources.getDimensionPixelSize(R.dimen.compose_media_preview_size) + imageView.context.resources.getDimensionPixelSize( + R.dimen.compose_media_preview_size + ) val layoutParams = ConstraintLayout.LayoutParams(thumbnailViewSize, thumbnailViewSize) val margin = itemView.context.resources .getDimensionPixelSize(R.dimen.compose_media_preview_margin) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt index 14dc21003..1db7982c6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt @@ -38,9 +38,9 @@ import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.util.isHttpNotFound import com.keylesspalace.tusky.util.parseAsMastodonHtml import com.keylesspalace.tusky.util.visible +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class DraftsActivity : BaseActivity(), DraftActionListener { @@ -74,7 +74,9 @@ class DraftsActivity : BaseActivity(), DraftActionListener { binding.draftsRecyclerView.adapter = adapter binding.draftsRecyclerView.layoutManager = LinearLayoutManager(this) - binding.draftsRecyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) + binding.draftsRecyclerView.addItemDecoration( + DividerItemDecoration(this, DividerItemDecoration.VERTICAL) + ) bottomSheet = BottomSheetBehavior.from(binding.bottomSheet.root) @@ -134,10 +136,18 @@ class DraftsActivity : BaseActivity(), DraftActionListener { if (throwable.isHttpNotFound()) { // the original status to which a reply was drafted has been deleted // let's open the ComposeActivity without reply information - Toast.makeText(context, getString(R.string.drafts_post_reply_removed), Toast.LENGTH_LONG).show() + Toast.makeText( + context, + getString(R.string.drafts_post_reply_removed), + Toast.LENGTH_LONG + ).show() openDraftWithoutReply(draft) } else { - Snackbar.make(binding.root, getString(R.string.drafts_failed_loading_reply), Snackbar.LENGTH_SHORT) + Snackbar.make( + binding.root, + getString(R.string.drafts_failed_loading_reply), + Snackbar.LENGTH_SHORT + ) .show() } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt index 1edf354d6..1c8ddbfed 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsAdapter.kt @@ -47,7 +47,10 @@ class DraftsAdapter( } ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { val binding = ItemDraftBinding.inflate(LayoutInflater.from(parent.context), parent, false) val viewHolder = BindingHolder(binding) @@ -77,7 +80,9 @@ class DraftsAdapter( holder.binding.content.text = draft.content holder.binding.draftMediaPreview.visible(draft.attachments.isNotEmpty()) - (holder.binding.draftMediaPreview.adapter as DraftMediaAdapter).submitList(draft.attachments) + (holder.binding.draftMediaPreview.adapter as DraftMediaAdapter).submitList( + draft.attachments + ) if (draft.poll != null) { holder.binding.draftPoll.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt index e748aebb9..813a424fa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsViewModel.kt @@ -26,8 +26,8 @@ import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.db.DraftEntity import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class DraftsViewModel @Inject constructor( val database: AppDatabase, @@ -38,7 +38,11 @@ class DraftsViewModel @Inject constructor( val drafts = Pager( config = PagingConfig(pageSize = 20), - pagingSourceFactory = { database.draftDao().draftsPagingSource(accountManager.activeAccount?.id!!) } + pagingSourceFactory = { + database.draftDao().draftsPagingSource( + accountManager.activeAccount?.id!! + ) + } ).flow .cachedIn(viewModelScope) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt index 6737acd3b..709e2c5f7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt @@ -29,9 +29,9 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isHttpNotFound import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible -import kotlinx.coroutines.launch import java.util.Date import javax.inject.Inject +import kotlinx.coroutines.launch class EditFilterActivity : BaseActivity() { @Inject @@ -115,7 +115,12 @@ class EditFilterActivity : BaseActivity() { ) } binding.filterDurationSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { viewModel.setDuration( if (originalFilter?.expiresAt == null) { position @@ -266,10 +271,16 @@ class EditFilterActivity : BaseActivity() { if (viewModel.saveChanges(this@EditFilterActivity)) { finish() // Possibly affected contexts: any context affected by the original filter OR any context affected by the updated filter - val affectedContexts = viewModel.contexts.value.map { it.kind }.union(originalFilter?.context ?: listOf()).distinct() + val affectedContexts = viewModel.contexts.value.map { + it.kind + }.union(originalFilter?.context ?: listOf()).distinct() eventHub.dispatch(FilterUpdatedEvent(affectedContexts)) } else { - Snackbar.make(binding.root, "Error saving filter '${viewModel.title.value}'", Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + "Error saving filter '${viewModel.title.value}'", + Snackbar.LENGTH_SHORT + ).show() } } } @@ -288,11 +299,19 @@ class EditFilterActivity : BaseActivity() { finish() }, { - Snackbar.make(binding.root, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + "Error deleting filter '${filter.title}'", + Snackbar.LENGTH_SHORT + ).show() } ) } else { - Snackbar.make(binding.root, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show() + Snackbar.make( + binding.root, + "Error deleting filter '${filter.title}'", + Snackbar.LENGTH_SHORT + ).show() } } ) @@ -307,7 +326,11 @@ class EditFilterActivity : BaseActivity() { // but create/edit take a number of seconds (relative to the time the operation is posted) fun getSecondsForDurationIndex(index: Int, context: Context?, default: Date? = null): Int? { return when (index) { - -1 -> if (default == null) { default } else { ((default.time - System.currentTimeMillis()) / 1000).toInt() } + -1 -> if (default == null) { + default + } else { + ((default.time - System.currentTimeMillis()) / 1000).toInt() + } 0 -> null else -> context?.resources?.getIntArray(R.array.filter_duration_values)?.get(index) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterViewModel.kt index d055c3177..f98d983af 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterViewModel.kt @@ -9,9 +9,9 @@ import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.FilterKeyword import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isHttpNotFound +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.withContext -import javax.inject.Inject class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub: EventHub) : ViewModel() { private var originalFilter: Filter? = null @@ -92,7 +92,13 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub } } - private suspend fun createFilter(title: String, contexts: List, action: String, durationIndex: Int, context: Context): Boolean { + private suspend fun createFilter( + title: String, + contexts: List, + action: String, + durationIndex: Int, + context: Context + ): Boolean { val expiresInSeconds = EditFilterActivity.getSecondsForDurationIndex(durationIndex, context) api.createFilter( title = title, @@ -103,7 +109,11 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub { newFilter -> // This is _terrible_, but the all-in-one update filter api Just Doesn't Work return keywords.value.map { keyword -> - api.addFilterKeyword(filterId = newFilter.id, keyword = keyword.keyword, wholeWord = keyword.wholeWord) + api.addFilterKeyword( + filterId = newFilter.id, + keyword = keyword.keyword, + wholeWord = keyword.wholeWord + ) }.none { it.isFailure } }, { throwable -> @@ -116,7 +126,14 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub ) } - private suspend fun updateFilter(originalFilter: Filter, title: String, contexts: List, action: String, durationIndex: Int, context: Context): Boolean { + private suspend fun updateFilter( + originalFilter: Filter, + title: String, + contexts: List, + action: String, + durationIndex: Int, + context: Context + ): Boolean { val expiresInSeconds = EditFilterActivity.getSecondsForDurationIndex(durationIndex, context) api.updateFilter( id = originalFilter.id, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/FilterExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/FilterExtensions.kt index 2a6c69d21..0f14bc5fd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/FilterExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/FilterExtensions.kt @@ -22,7 +22,9 @@ import androidx.appcompat.app.AlertDialog import com.keylesspalace.tusky.R import com.keylesspalace.tusky.util.await -internal suspend fun Activity.showDeleteFilterDialog(filterTitle: String) = AlertDialog.Builder(this) +internal suspend fun Activity.showDeleteFilterDialog(filterTitle: String) = AlertDialog.Builder( + this +) .setMessage(getString(R.string.dialog_delete_filter_text, filterTitle)) .setCancelable(true) .create() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt index d66fb7ad2..cc3afc70e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt @@ -14,8 +14,8 @@ import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class FiltersActivity : BaseActivity(), FiltersListener { @Inject @@ -54,20 +54,30 @@ class FiltersActivity : BaseActivity(), FiltersListener { private fun observeViewModel() { lifecycleScope.launch { viewModel.state.collect { state -> - binding.progressBar.visible(state.loadingState == FiltersViewModel.LoadingState.LOADING) + binding.progressBar.visible( + state.loadingState == FiltersViewModel.LoadingState.LOADING + ) binding.swipeRefreshLayout.isRefreshing = state.loadingState == FiltersViewModel.LoadingState.LOADING - binding.addFilterButton.visible(state.loadingState == FiltersViewModel.LoadingState.LOADED) + binding.addFilterButton.visible( + state.loadingState == FiltersViewModel.LoadingState.LOADED + ) when (state.loadingState) { FiltersViewModel.LoadingState.INITIAL, FiltersViewModel.LoadingState.LOADING -> binding.messageView.hide() FiltersViewModel.LoadingState.ERROR_NETWORK -> { - binding.messageView.setup(R.drawable.errorphant_offline, R.string.error_network) { + binding.messageView.setup( + R.drawable.errorphant_offline, + R.string.error_network + ) { loadFilters() } binding.messageView.show() } FiltersViewModel.LoadingState.ERROR_OTHER -> { - binding.messageView.setup(R.drawable.errorphant_error, R.string.error_generic) { + binding.messageView.setup( + R.drawable.errorphant_error, + R.string.error_generic + ) { loadFilters() } binding.messageView.show() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersAdapter.kt index f6e6791a7..96d51fc6e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersAdapter.kt @@ -14,8 +14,13 @@ class FiltersAdapter(val listener: FiltersListener, val filters: List) : override fun getItemCount(): Int = filters.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - return BindingHolder(ItemRemovableBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + return BindingHolder( + ItemRemovableBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) } override fun onBindViewHolder(holder: BindingHolder, position: Int) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersViewModel.kt index 5baade6b5..315af81db 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersViewModel.kt @@ -10,10 +10,10 @@ import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isHttpNotFound +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch -import javax.inject.Inject class FiltersViewModel @Inject constructor( private val api: MastodonApi, @@ -21,7 +21,11 @@ class FiltersViewModel @Inject constructor( ) : ViewModel() { enum class LoadingState { - INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER + INITIAL, + LOADING, + LOADED, + ERROR_NETWORK, + ERROR_OTHER } data class State(val filters: List, val loadingState: LoadingState) @@ -61,7 +65,12 @@ class FiltersViewModel @Inject constructor( viewModelScope.launch { api.deleteFilter(filter.id).fold( { - this@FiltersViewModel._state.value = State(this@FiltersViewModel._state.value.filters.filter { it.id != filter.id }, LoadingState.LOADED) + this@FiltersViewModel._state.value = State( + this@FiltersViewModel._state.value.filters.filter { + it.id != filter.id + }, + LoadingState.LOADED + ) for (context in filter.context) { eventHub.dispatch(PreferenceChangedEvent(context)) } @@ -70,14 +79,27 @@ class FiltersViewModel @Inject constructor( if (throwable.isHttpNotFound()) { api.deleteFilterV1(filter.id).fold( { - this@FiltersViewModel._state.value = State(this@FiltersViewModel._state.value.filters.filter { it.id != filter.id }, LoadingState.LOADED) + this@FiltersViewModel._state.value = State( + this@FiltersViewModel._state.value.filters.filter { + it.id != filter.id + }, + LoadingState.LOADED + ) }, { - Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show() + Snackbar.make( + parent, + "Error deleting filter '${filter.title}'", + Snackbar.LENGTH_SHORT + ).show() } ) } else { - Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show() + Snackbar.make( + parent, + "Error deleting filter '${filter.title}'", + Snackbar.LENGTH_SHORT + ).show() } } ) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt index b6b56d4a3..82a17265d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt @@ -29,9 +29,9 @@ import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class FollowedTagsActivity : BaseActivity(), @@ -81,7 +81,9 @@ class FollowedTagsActivity : binding.followedTagsView.adapter = adapter binding.followedTagsView.setHasFixedSize(true) binding.followedTagsView.layoutManager = LinearLayoutManager(this) - binding.followedTagsView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) + binding.followedTagsView.addItemDecoration( + DividerItemDecoration(this, DividerItemDecoration.VERTICAL) + ) (binding.followedTagsView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false val hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false) @@ -101,7 +103,9 @@ class FollowedTagsActivity : private fun setupAdapter(): FollowedTagsAdapter { return FollowedTagsAdapter(this, viewModel).apply { addLoadStateListener { loadState -> - binding.followedTagsProgressBar.visible(loadState.refresh == LoadState.Loading && itemCount == 0) + binding.followedTagsProgressBar.visible( + loadState.refresh == LoadState.Loading && itemCount == 0 + ) if (loadState.refresh is LoadState.Error) { binding.followedTagsView.hide() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsAdapter.kt index 4cdc9f97a..211be5630 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsAdapter.kt @@ -15,13 +15,22 @@ class FollowedTagsAdapter( private val actionListener: HashtagActionListener, private val viewModel: FollowedTagsViewModel ) : PagingDataAdapter>(STRING_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder = - BindingHolder(ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder = BindingHolder( + ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) - override fun onBindViewHolder(holder: BindingHolder, position: Int) { + override fun onBindViewHolder( + holder: BindingHolder, + position: Int + ) { viewModel.tags[position].let { tag -> holder.itemView.findViewById(R.id.followed_tag).text = tag.name - holder.itemView.findViewById(R.id.followed_tag_unfollow).setOnClickListener { + holder.itemView.findViewById( + R.id.followed_tag_unfollow + ).setOnClickListener { actionListener.unfollow(tag.name, holder.bindingAdapterPosition) } } @@ -31,8 +40,10 @@ class FollowedTagsAdapter( companion object { val STRING_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem - override fun areContentsTheSame(oldItem: String, newItem: String): Boolean = oldItem == newItem + override fun areItemsTheSame(oldItem: String, newItem: String): Boolean = + oldItem == newItem + override fun areContentsTheSame(oldItem: String, newItem: String): Boolean = + oldItem == newItem } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt index 1a1b794bb..63170b59d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsViewModel.kt @@ -35,10 +35,16 @@ class FollowedTagsViewModel @Inject constructor( } ).flow.cachedIn(viewModelScope) - fun searchAutocompleteSuggestions(token: String): List { + fun searchAutocompleteSuggestions( + token: String + ): List { return api.searchSync(query = token, type = SearchType.Hashtag.apiParameter, limit = 10) .fold({ searchResult -> - searchResult.hashtags.map { ComposeAutoCompleteAdapter.AutocompleteResult.HashtagResult(it.name) } + searchResult.hashtags.map { + ComposeAutoCompleteAdapter.AutocompleteResult.HashtagResult( + it.name + ) + } }, { e -> Log.e(TAG, "Autocomplete search for $token failed.", e) emptyList() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt b/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt index c781ff90e..bf9f07f43 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/instanceinfo/InstanceInfoRepository.kt @@ -26,9 +26,9 @@ import com.keylesspalace.tusky.db.InstanceInfoEntity import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isHttpNotFound +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import javax.inject.Inject class InstanceInfoRepository @Inject constructor( private val api: MastodonApi, @@ -77,7 +77,7 @@ class InstanceInfoRepository @Inject constructor( maxMediaAttachments = instance.configuration.statuses?.maxMediaAttachments ?: DEFAULT_MAX_MEDIA_ATTACHMENTS, maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields, maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength, - maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength, + maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength ) dao.upsert(instanceEntity) instanceEntity @@ -86,7 +86,11 @@ class InstanceInfoRepository @Inject constructor( if (throwable.isHttpNotFound()) { getInstanceInfoV1() } else { - Log.w(TAG, "failed to instance, falling back to cache and default values", throwable) + Log.w( + TAG, + "failed to instance, falling back to cache and default values", + throwable + ) dao.getInstanceInfo(instanceName) } } @@ -105,7 +109,7 @@ class InstanceInfoRepository @Inject constructor( maxFields = instanceInfo?.maxFields ?: DEFAULT_MAX_ACCOUNT_FIELDS, maxFieldNameLength = instanceInfo?.maxFieldNameLength, maxFieldValueLength = instanceInfo?.maxFieldValueLength, - version = instanceInfo?.version, + version = instanceInfo?.version ) } } @@ -129,13 +133,17 @@ class InstanceInfoRepository @Inject constructor( maxMediaAttachments = instance.configuration?.statuses?.maxMediaAttachments ?: instance.maxMediaAttachments, maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields, maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength, - maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength, + maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength ) dao.upsert(instanceEntity) instanceEntity }, { throwable -> - Log.w(TAG, "failed to instance, falling back to cache and default values", throwable) + Log.w( + TAG, + "failed to instance, falling back to cache and default values", + throwable + ) dao.getInstanceInfo(instanceName) } ) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt index 7e0476896..06484d245 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt @@ -42,9 +42,9 @@ import com.keylesspalace.tusky.util.openLinkInCustomTab import com.keylesspalace.tusky.util.rickRoll import com.keylesspalace.tusky.util.shouldRickRoll import com.keylesspalace.tusky.util.viewBinding +import javax.inject.Inject import kotlinx.coroutines.launch import okhttp3.HttpUrl -import javax.inject.Inject /** Main login page, the first thing that users see. Has prompt for instance and login button. */ class LoginActivity : BaseActivity(), Injectable { @@ -201,7 +201,11 @@ class LoginActivity : BaseActivity(), Injectable { } } - private fun redirectUserToAuthorizeAndLogin(domain: String, clientId: String, openInWebView: Boolean) { + private fun redirectUserToAuthorizeAndLogin( + domain: String, + clientId: String, + openInWebView: Boolean + ) { // To authorize this app and log in it's necessary to redirect to the domain given, // login there, and the server will redirect back to the app with its response. val uri = HttpUrl.Builder() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt index ed5744165..75735b471 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt @@ -45,9 +45,9 @@ import com.keylesspalace.tusky.di.ViewModelFactory import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible +import javax.inject.Inject import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize -import javax.inject.Inject /** Contract for starting [LoginWebViewActivity]. */ class OauthLogin : ActivityResultContract() { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewViewModel.kt index cf3c6a6bd..88328b289 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewViewModel.kt @@ -21,9 +21,9 @@ import androidx.lifecycle.viewModelScope import at.connyduck.calladapter.networkresult.fold import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.isHttpNotFound +import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch -import javax.inject.Inject class LoginWebViewViewModel @Inject constructor( private val api: MastodonApi @@ -48,11 +48,19 @@ class LoginWebViewViewModel @Inject constructor( instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty() }, { throwable -> - Log.w("LoginWebViewViewModel", "failed to load instance info", throwable) + Log.w( + "LoginWebViewViewModel", + "failed to load instance info", + throwable + ) } ) } else { - Log.w("LoginWebViewViewModel", "failed to load instance info", throwable) + Log.w( + "LoginWebViewViewModel", + "failed to load instance info", + throwable + ) } } ) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt index d735af3c1..3d73ec0eb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationFetcher.kt @@ -14,10 +14,10 @@ import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.isLessThan -import kotlinx.coroutines.delay import javax.inject.Inject import kotlin.math.min import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.delay /** Models next/prev links from the "Links" header in an API response */ data class Links(val next: String?, val prev: String?) { @@ -55,12 +55,16 @@ class NotificationFetcher @Inject constructor( for (account in accountManager.getAllAccountsOrderedByActive()) { if (account.notificationsEnabled) { try { - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = context.getSystemService( + Context.NOTIFICATION_SERVICE + ) as NotificationManager // Create sorted list of new notifications val notifications = fetchNewNotifications(account) .filter { filterNotification(notificationManager, account, it) } - .sortedWith(compareBy({ it.id.length }, { it.id })) // oldest notifications first + .sortedWith( + compareBy({ it.id.length }, { it.id }) + ) // oldest notifications first .toMutableList() // TODO do this before filter above? But one could argue that (for example) a tab badge is also a notification @@ -74,13 +78,18 @@ class NotificationFetcher @Inject constructor( // Err on the side of removing *older* notifications to make room for newer // notifications. val currentAndroidNotifications = notificationManager.activeNotifications - .sortedWith(compareBy({ it.tag.length }, { it.tag })) // oldest notifications first + .sortedWith( + compareBy({ it.tag.length }, { it.tag }) + ) // oldest notifications first // Check to see if any notifications need to be removed val toRemove = currentAndroidNotifications.size + notifications.size - MAX_NOTIFICATIONS if (toRemove > 0) { // Prefer to cancel old notifications first - currentAndroidNotifications.subList(0, min(toRemove, currentAndroidNotifications.size)) + currentAndroidNotifications.subList( + 0, + min(toRemove, currentAndroidNotifications.size) + ) .forEach { notificationManager.cancel(it.tag, it.id) } // Still got notifications to remove? Trim the list of new notifications, @@ -106,7 +115,11 @@ class NotificationFetcher @Inject constructor( account, notificationsGroup.value.size == 1 ) - notificationManager.notify(notification.id, account.id.toInt(), androidNotification) + notificationManager.notify( + notification.id, + account.id.toInt(), + androidNotification + ) // Android will rate limit / drop notifications if they're posted too // quickly. There is no indication to the user that this happened. @@ -158,7 +171,14 @@ class NotificationFetcher @Inject constructor( Log.d(TAG, "getting notification marker for ${account.fullName}") val remoteMarkerId = fetchMarker(authHeader, account)?.lastReadId ?: "0" val localMarkerId = account.notificationMarkerId - val markerId = if (remoteMarkerId.isLessThan(localMarkerId)) localMarkerId else remoteMarkerId + val markerId = if (remoteMarkerId.isLessThan( + localMarkerId + ) + ) { + localMarkerId + } else { + remoteMarkerId + } val readingPosition = account.lastNotificationId var minId: String? = if (readingPosition.isLessThan(markerId)) markerId else readingPosition diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt deleted file mode 100644 index e69de29bb..000000000 diff --git a/app/src/main/java/com/keylesspalace/tusky/components/notifications/PushNotificationHelper.kt b/app/src/main/java/com/keylesspalace/tusky/components/notifications/PushNotificationHelper.kt index f61307e3e..c89823e6d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/notifications/PushNotificationHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/notifications/PushNotificationHelper.kt @@ -66,7 +66,9 @@ fun showMigrationNoticeIfNecessary( Snackbar.make(parent, R.string.tips_push_notification_migration, Snackbar.LENGTH_INDEFINITE) .setAnchorView(anchorView) - .setAction(R.string.action_details) { showMigrationExplanationDialog(context, accountManager) } + .setAction( + R.string.action_details + ) { showMigrationExplanationDialog(context, accountManager) } .show() } @@ -75,7 +77,9 @@ private fun showMigrationExplanationDialog(context: Context, accountManager: Acc if (currentAccountNeedsMigration(accountManager)) { setMessage(R.string.dialog_push_notification_migration) setPositiveButton(R.string.title_migration_relogin) { _, _ -> - context.startActivity(LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION)) + context.startActivity( + LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION) + ) } } else { setMessage(R.string.dialog_push_notification_migration_other_accounts) @@ -89,12 +93,21 @@ private fun showMigrationExplanationDialog(context: Context, accountManager: Acc } } -private suspend fun enableUnifiedPushNotificationsForAccount(context: Context, api: MastodonApi, accountManager: AccountManager, account: AccountEntity) { +private suspend fun enableUnifiedPushNotificationsForAccount( + context: Context, + api: MastodonApi, + accountManager: AccountManager, + account: AccountEntity +) { if (isUnifiedPushNotificationEnabledForAccount(account)) { // Already registered, update the subscription to match notification settings updateUnifiedPushSubscription(context, api, accountManager, account) } else { - UnifiedPush.registerAppWithDialog(context, account.id.toString(), features = arrayListOf(UnifiedPush.FEATURE_BYTES_MESSAGE)) + UnifiedPush.registerAppWithDialog( + context, + account.id.toString(), + features = arrayListOf(UnifiedPush.FEATURE_BYTES_MESSAGE) + ) } } @@ -116,7 +129,11 @@ private fun isUnifiedPushAvailable(context: Context): Boolean = fun canEnablePushNotifications(context: Context, accountManager: AccountManager): Boolean = isUnifiedPushAvailable(context) && !anyAccountNeedsMigration(accountManager) -suspend fun enablePushNotificationsWithFallback(context: Context, api: MastodonApi, accountManager: AccountManager) { +suspend fun enablePushNotificationsWithFallback( + context: Context, + api: MastodonApi, + accountManager: AccountManager +) { if (!canEnablePushNotifications(context, accountManager)) { // No UP distributors NotificationHelper.enablePullNotifications(context) @@ -151,9 +168,14 @@ fun disableAllNotifications(context: Context, accountManager: AccountManager) { private fun buildSubscriptionData(context: Context, account: AccountEntity): Map = buildMap { - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = context.getSystemService( + Context.NOTIFICATION_SERVICE + ) as NotificationManager Notification.Type.visibleTypes.forEach { - put("data[alerts][${it.presentation}]", NotificationHelper.filterNotification(notificationManager, account, it)) + put( + "data[alerts][${it.presentation}]", + NotificationHelper.filterNotification(notificationManager, account, it) + ) } } @@ -196,7 +218,12 @@ suspend fun registerUnifiedPushEndpoint( } // Synchronize the enabled / disabled state of notifications with server-side subscription -suspend fun updateUnifiedPushSubscription(context: Context, api: MastodonApi, accountManager: AccountManager, account: AccountEntity) { +suspend fun updateUnifiedPushSubscription( + context: Context, + api: MastodonApi, + accountManager: AccountManager, + account: AccountEntity +) { withContext(Dispatchers.IO) { api.updatePushNotificationSubscription( "Bearer ${account.accessToken}", @@ -211,7 +238,11 @@ suspend fun updateUnifiedPushSubscription(context: Context, api: MastodonApi, ac } } -suspend fun unregisterUnifiedPushEndpoint(api: MastodonApi, accountManager: AccountManager, account: AccountEntity) { +suspend fun unregisterUnifiedPushEndpoint( + api: MastodonApi, + accountManager: AccountManager, + account: AccountEntity +) { withContext(Dispatchers.IO) { api.unsubscribePushNotifications("Bearer ${account.accessToken}", account.domain) .onFailure { throwable -> diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt index 4247a0a75..bdbed2fc5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt @@ -56,10 +56,10 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeRes +import javax.inject.Inject import retrofit2.Call import retrofit2.Callback import retrofit2.Response -import javax.inject.Inject class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject @@ -74,7 +74,11 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var accountPreferenceDataStore: AccountPreferenceDataStore - private val iconSize by unsafeLazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } + private val iconSize by unsafeLazy { + resources.getDimensionPixelSize( + R.dimen.preference_icon_size + ) + } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { val context = requireContext() @@ -198,14 +202,17 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { value = visibility.serverString() setIcon(getIconForVisibility(visibility)) setOnPreferenceChangeListener { _, newValue -> - setIcon(getIconForVisibility(Status.Visibility.byString(newValue as String))) + setIcon( + getIconForVisibility(Status.Visibility.byString(newValue as String)) + ) syncWithServer(visibility = newValue) true } } listPreference { - val locales = getLocaleList(getInitialLanguages(null, accountManager.activeAccount)) + val locales = + getLocaleList(getInitialLanguages(null, accountManager.activeAccount)) setTitle(R.string.pref_default_post_language) // Explicitly add "System default" to the start of the list entries = ( @@ -289,14 +296,21 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { startActivity(intent) } else { activity?.let { - val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES) + val intent = PreferencesActivity.newIntent( + it, + PreferencesActivity.NOTIFICATION_PREFERENCES + ) it.startActivity(intent) it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) } } } - private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null, language: String? = null) { + private fun syncWithServer( + visibility: String? = null, + sensitive: Boolean? = null, + language: String? = null + ) { // TODO these could also be "datastore backed" preferences (a ServerPreferenceDataStore); follow-up of issue #3204 mastodonApi.accountUpdateSource(visibility, sensitive, language) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt index 9b79f3ce7..478e07ce8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt @@ -40,8 +40,8 @@ import com.keylesspalace.tusky.util.getNonNullString import com.keylesspalace.tusky.util.setAppNightMode import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class PreferencesActivity : BaseActivity(), @@ -127,12 +127,16 @@ class PreferencesActivity : override fun onResume() { super.onResume() - PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this) + PreferenceManager.getDefaultSharedPreferences( + this + ).registerOnSharedPreferenceChangeListener(this) } override fun onPause() { super.onPause() - PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this) + PreferenceManager.getDefaultSharedPreferences( + this + ).unregisterOnSharedPreferenceChangeListener(this) } private fun saveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt index b01b320f4..a0c54ffe4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt @@ -49,7 +49,11 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var localeManager: LocaleManager - private val iconSize by unsafeLazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } + private val iconSize by unsafeLazy { + resources.getDimensionPixelSize( + R.dimen.preference_icon_size + ) + } enum class ReadingOrder { /** User scrolls up, reading statuses oldest to newest */ @@ -253,7 +257,9 @@ class PreferencesFragment : PreferenceFragmentCompat(), Injectable { key = PrefKeys.WELLBEING_LIMITED_NOTIFICATIONS setOnPreferenceChangeListener { _, value -> for (account in accountManager.accounts) { - val notificationFilter = deserialize(account.notificationsFilter).toMutableSet() + val notificationFilter = deserialize( + account.notificationsFilter + ).toMutableSet() if (value == true) { notificationFilter.add(Notification.Type.FAVOURITE) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt index da63db127..84f1336f1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt @@ -59,7 +59,10 @@ class ProxyPreferencesFragment : PreferenceFragmentCompat() { MAX_PROXY_PORT ) - validatedEditTextPreference(portErrorMessage, ProxyConfiguration::isValidProxyPort) { + validatedEditTextPreference( + portErrorMessage, + ProxyConfiguration::isValidProxyPort + ) { setTitle(R.string.pref_title_http_proxy_port) key = PrefKeys.HTTP_PROXY_PORT isIconSpaceReserved = false diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt index 6f1ee6dc1..1baf15784 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt @@ -46,7 +46,9 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector { val accountId = intent?.getStringExtra(ACCOUNT_ID) val accountUserName = intent?.getStringExtra(ACCOUNT_USERNAME) if (accountId.isNullOrBlank() || accountUserName.isNullOrBlank()) { - throw IllegalStateException("accountId ($accountId) or accountUserName ($accountUserName) is null") + throw IllegalStateException( + "accountId ($accountId) or accountUserName ($accountUserName) is null" + ) } viewModel.init(accountId, accountUserName, intent?.getStringExtra(STATUS_ID)) @@ -130,13 +132,17 @@ class ReportActivity : BottomSheetActivity(), HasAndroidInjector { private const val STATUS_ID = "status_id" @JvmStatic - fun getIntent(context: Context, accountId: String, userName: String, statusId: String? = null) = - Intent(context, ReportActivity::class.java) - .apply { - putExtra(ACCOUNT_ID, accountId) - putExtra(ACCOUNT_USERNAME, userName) - putExtra(STATUS_ID, statusId) - } + fun getIntent( + context: Context, + accountId: String, + userName: String, + statusId: String? = null + ) = Intent(context, ReportActivity::class.java) + .apply { + putExtra(ACCOUNT_ID, accountId) + putExtra(ACCOUNT_USERNAME, userName) + putExtra(STATUS_ID, statusId) + } } override fun androidInjector() = androidInjector diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt index f30726a23..96b169c3f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt @@ -37,12 +37,12 @@ import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.toViewData +import javax.inject.Inject import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import javax.inject.Inject class ReportViewModel @Inject constructor( private val mastodonApi: MastodonApi, @@ -196,7 +196,12 @@ class ReportViewModel @Inject constructor( fun doReport() { reportingStateMutable.value = Loading() viewModelScope.launch { - mastodonApi.report(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null) + mastodonApi.report( + accountId, + selectedIds.toList(), + reportNote, + if (isRemoteAccount) isRemoteNotify else null + ) .fold({ reportingStateMutable.value = Success(true) }, { error -> diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/Screen.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/Screen.kt index e3c5c197d..fb0b15cae 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/Screen.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/Screen.kt @@ -20,5 +20,5 @@ enum class Screen { Note, Done, Back, - Finish, + Finish } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt index 21e1f51a0..f8db53107 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusViewHolder.kt @@ -50,7 +50,9 @@ class StatusViewHolder( private val getStatusForPosition: (Int) -> StatusViewData.Concrete? ) : RecyclerView.ViewHolder(binding.root) { - private val mediaViewHeight = itemView.context.resources.getDimensionPixelSize(R.dimen.status_media_preview_height) + private val mediaViewHeight = itemView.context.resources.getDimensionPixelSize( + R.dimen.status_media_preview_height + ) private val statusViewHelper = StatusViewHelper(itemView) private val absoluteTimeFormatter = AbsoluteTimeFormatter() @@ -93,7 +95,11 @@ class StatusViewHolder( mediaViewHeight ) - statusViewHelper.setupPollReadonly(viewData.status.poll.toViewData(), viewData.status.emojis, statusDisplayOptions) + statusViewHelper.setupPollReadonly( + viewData.status.poll.toViewData(), + viewData.status.emojis, + statusDisplayOptions + ) setCreatedAt(viewData.status.createdAt) } @@ -107,11 +113,22 @@ class StatusViewHolder( ) if (viewdata.status.spoilerText.isBlank()) { - setTextVisible(true, viewdata.content, viewdata.status.mentions, viewdata.status.tags, viewdata.status.emojis, adapterHandler) + setTextVisible( + true, + viewdata.content, + viewdata.status.mentions, + viewdata.status.tags, + viewdata.status.emojis, + adapterHandler + ) binding.statusContentWarningButton.hide() binding.statusContentWarningDescription.hide() } else { - val emojiSpoiler = viewdata.status.spoilerText.emojify(viewdata.status.emojis, binding.statusContentWarningDescription, statusDisplayOptions.animateEmojis) + val emojiSpoiler = viewdata.status.spoilerText.emojify( + viewdata.status.emojis, + binding.statusContentWarningDescription, + statusDisplayOptions.animateEmojis + ) binding.statusContentWarningDescription.text = emojiSpoiler binding.statusContentWarningDescription.show() binding.statusContentWarningButton.show() @@ -121,11 +138,25 @@ class StatusViewHolder( val contentShown = viewState.isContentShow(viewdata.id, true) binding.statusContentWarningDescription.invalidate() viewState.setContentShow(viewdata.id, !contentShown) - setTextVisible(!contentShown, viewdata.content, viewdata.status.mentions, viewdata.status.tags, viewdata.status.emojis, adapterHandler) + setTextVisible( + !contentShown, + viewdata.content, + viewdata.status.mentions, + viewdata.status.tags, + viewdata.status.emojis, + adapterHandler + ) setContentWarningButtonText(!contentShown) } } - setTextVisible(viewState.isContentShow(viewdata.id, true), viewdata.content, viewdata.status.mentions, viewdata.status.tags, viewdata.status.emojis, adapterHandler) + setTextVisible( + viewState.isContentShow(viewdata.id, true), + viewdata.content, + viewdata.status.mentions, + viewdata.status.tags, + viewdata.status.emojis, + adapterHandler + ) } } } @@ -147,7 +178,11 @@ class StatusViewHolder( listener: LinkListener ) { if (expanded) { - val emojifiedText = content.emojify(emojis, binding.statusContent, statusDisplayOptions.animateEmojis) + val emojifiedText = content.emojify( + emojis, + binding.statusContent, + statusDisplayOptions.animateEmojis + ) setClickableText(binding.statusContent, emojifiedText, mentions, tags, listener) } else { setClickableMentions(binding.statusContent, mentions, listener) @@ -174,7 +209,12 @@ class StatusViewHolder( } } - private fun setupCollapsedState(collapsible: Boolean, collapsed: Boolean, expanded: Boolean, spoilerText: String) { + private fun setupCollapsedState( + collapsible: Boolean, + collapsed: Boolean, + expanded: Boolean, + spoilerText: String + ) { /* input filter for TextViews have to be set before text */ if (collapsible && (expanded || TextUtils.isEmpty(spoilerText))) { binding.buttonToggleContent.setOnClickListener { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesAdapter.kt index 7e2c24174..db5c9bb96 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesAdapter.kt @@ -36,7 +36,11 @@ class StatusesAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusViewHolder { - val binding = ItemReportStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ItemReportStatusBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return StatusViewHolder( binding, statusDisplayOptions, @@ -54,11 +58,15 @@ class StatusesAdapter( companion object { val STATUS_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean = - oldItem == newItem + override fun areContentsTheSame( + oldItem: StatusViewData.Concrete, + newItem: StatusViewData.Concrete + ): Boolean = oldItem == newItem - override fun areItemsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean = - oldItem.id == newItem.id + override fun areItemsTheSame( + oldItem: StatusViewData.Concrete, + newItem: StatusViewData.Concrete + ): Boolean = oldItem.id == newItem.id } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesPagingSource.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesPagingSource.kt index c007239d8..2d2be224a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesPagingSource.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/adapter/StatusesPagingSource.kt @@ -42,7 +42,8 @@ class StatusesPagingSource( val result = if (params is LoadParams.Refresh && key != null) { withContext(Dispatchers.IO) { val initialStatus = async { getSingleStatus(key) } - val additionalStatuses = async { getStatusList(maxId = key, limit = params.loadSize - 1) } + val additionalStatuses = + async { getStatusList(maxId = key, limit = params.loadSize - 1) } listOf(initialStatus.await()) + additionalStatuses.await() } } else { @@ -75,7 +76,11 @@ class StatusesPagingSource( return mastodonApi.statusObservable(statusId).await() } - private suspend fun getStatusList(minId: String? = null, maxId: String? = null, limit: Int): List { + private suspend fun getStatusList( + minId: String? = null, + maxId: String? = null, + limit: Int + ): List { return mastodonApi.accountStatusesObservable( accountId = accountId, maxId = maxId, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportNoteFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportNoteFragment.kt index d77685e64..1b303d7dd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportNoteFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportNoteFragment.kt @@ -95,7 +95,11 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable { binding.buttonBack.isEnabled = true binding.progressBar.hide() - Snackbar.make(binding.buttonBack, if (error is IOException) R.string.error_network else R.string.error_generic, Snackbar.LENGTH_LONG) + Snackbar.make( + binding.buttonBack, + if (error is IOException) R.string.error_network else R.string.error_generic, + Snackbar.LENGTH_LONG + ) .setAction(R.string.action_retry) { sendReport() } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt index 6165d20f0..9f20265f1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt @@ -59,9 +59,9 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class ReportStatusesFragment : Fragment(R.layout.fragment_report_statuses), @@ -93,7 +93,11 @@ class ReportStatusesFragment : if (v != null) { val url = actionable.attachments[idx].url ViewCompat.setTransitionName(v, url) - val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), v, url) + val options = ActivityOptionsCompat.makeSceneTransitionAnimation( + requireActivity(), + v, + url + ) startActivity(intent, options.toBundle()) } else { startActivity(intent) @@ -164,7 +168,9 @@ class ReportStatusesFragment : adapter = StatusesAdapter(statusDisplayOptions, viewModel.statusViewState, this) - binding.recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) + binding.recyclerView.addItemDecoration( + DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL) + ) binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) binding.recyclerView.adapter = adapter (binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false @@ -185,7 +191,9 @@ class ReportStatusesFragment : binding.progressBarBottom.visible(loadState.append == LoadState.Loading) binding.progressBarTop.visible(loadState.prepend == LoadState.Loading) - binding.progressBarLoading.visible(loadState.refresh == LoadState.Loading && !binding.swipeRefreshLayout.isRefreshing) + binding.progressBarLoading.visible( + loadState.refresh == LoadState.Loading && !binding.swipeRefreshLayout.isRefreshing + ) if (loadState.refresh != LoadState.Loading) { binding.swipeRefreshLayout.isRefreshing = false @@ -221,9 +229,13 @@ class ReportStatusesFragment : return viewModel.isStatusChecked(id) } - override fun onViewAccount(id: String) = startActivity(AccountActivity.getIntent(requireContext(), id)) + override fun onViewAccount(id: String) = startActivity( + AccountActivity.getIntent(requireContext(), id) + ) - override fun onViewTag(tag: String) = startActivity(StatusListActivity.newHashtagIntent(requireContext(), tag)) + override fun onViewTag(tag: String) = startActivity( + StatusListActivity.newHashtagIntent(requireContext(), tag) + ) override fun onViewUrl(url: String) = viewModel.checkClickedUrl(url) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/model/StatusViewState.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/model/StatusViewState.kt index 2bcade2fe..893838c60 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/model/StatusViewState.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/model/StatusViewState.kt @@ -20,17 +20,35 @@ class StatusViewState { private val contentShownState = HashMap() private val longContentCollapsedState = HashMap() - fun isMediaShow(id: String, isSensitive: Boolean): Boolean = isStateEnabled(mediaShownState, id, !isSensitive) + fun isMediaShow(id: String, isSensitive: Boolean): Boolean = isStateEnabled( + mediaShownState, + id, + !isSensitive + ) fun setMediaShow(id: String, isShow: Boolean) = setStateEnabled(mediaShownState, id, isShow) - fun isContentShow(id: String, isSensitive: Boolean): Boolean = isStateEnabled(contentShownState, id, !isSensitive) + fun isContentShow(id: String, isSensitive: Boolean): Boolean = isStateEnabled( + contentShownState, + id, + !isSensitive + ) fun setContentShow(id: String, isShow: Boolean) = setStateEnabled(contentShownState, id, isShow) - fun isCollapsed(id: String, isCollapsed: Boolean): Boolean = isStateEnabled(longContentCollapsedState, id, isCollapsed) - fun setCollapsed(id: String, isCollapsed: Boolean) = setStateEnabled(longContentCollapsedState, id, isCollapsed) + fun isCollapsed(id: String, isCollapsed: Boolean): Boolean = isStateEnabled( + longContentCollapsedState, + id, + isCollapsed + ) + fun setCollapsed(id: String, isCollapsed: Boolean) = + setStateEnabled(longContentCollapsedState, id, isCollapsed) - private fun isStateEnabled(map: Map, id: String, def: Boolean): Boolean = map[id] - ?: def + private fun isStateEnabled(map: Map, id: String, def: Boolean): Boolean = + map[id] + ?: def - private fun setStateEnabled(map: MutableMap, id: String, state: Boolean) = map.put(id, state) + private fun setStateEnabled(map: MutableMap, id: String, state: Boolean) = + map.put( + id, + state + ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt index d7e72812c..89da2e12d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt @@ -45,9 +45,9 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class ScheduledStatusActivity : BaseActivity(), @@ -109,7 +109,10 @@ class ScheduledStatusActivity : if (loadState.refresh is LoadState.NotLoading) { binding.progressBar.hide() if (adapter.itemCount == 0) { - binding.errorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_scheduled_posts) + binding.errorMessageView.setup( + R.drawable.elephant_friend_empty, + R.string.no_scheduled_posts + ) binding.errorMessageView.show() } else { binding.errorMessageView.hide() @@ -163,8 +166,8 @@ class ScheduledStatusActivity : visibility = item.params.visibility, scheduledAt = item.scheduledAt, sensitive = item.params.sensitive, - kind = ComposeActivity.ComposeKind.EDIT_SCHEDULED, - ), + kind = ComposeActivity.ComposeKind.EDIT_SCHEDULED + ) ) startActivity(intent) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusAdapter.kt index 9d51bc53c..a13a41e47 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusAdapter.kt @@ -36,18 +36,31 @@ class ScheduledStatusAdapter( return oldItem.id == newItem.id } - override fun areContentsTheSame(oldItem: ScheduledStatus, newItem: ScheduledStatus): Boolean { + override fun areContentsTheSame( + oldItem: ScheduledStatus, + newItem: ScheduledStatus + ): Boolean { return oldItem == newItem } } ) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { - val binding = ItemScheduledStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { + val binding = ItemScheduledStatusBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) return BindingHolder(binding) } - override fun onBindViewHolder(holder: BindingHolder, position: Int) { + override fun onBindViewHolder( + holder: BindingHolder, + position: Int + ) { getItem(position)?.let { item -> holder.binding.edit.isEnabled = true holder.binding.delete.isEnabled = true diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt index 483c8e4df..821364b9e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusViewModel.kt @@ -25,8 +25,8 @@ import at.connyduck.calladapter.networkresult.fold import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.entity.ScheduledStatus import com.keylesspalace.tusky.network.MastodonApi -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class ScheduledStatusViewModel @Inject constructor( val mastodonApi: MastodonApi, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt index 01b69d38b..1b3de0f88 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt @@ -119,7 +119,13 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector, MenuProvider, private fun setupSearchView(searchView: SearchView) { searchView.setIconifiedByDefault(false) - searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName)) + searchView.setSearchableInfo( + ( + getSystemService( + Context.SEARCH_SERVICE + ) as? SearchManager + )?.getSearchableInfo(componentName) + ) // SearchView has a bug. If it's displayed 'app:showAsAction="always"' it's too wide, // pushing other icons (including the options menu '...' icon) off the edge of the diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index 0ab4d1e1c..1dd8d85a6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -33,10 +33,10 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.usecase.TimelineCases import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.viewdata.StatusViewData +import javax.inject.Inject import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.launch -import javax.inject.Inject class SearchViewModel @Inject constructor( mastodonApi: MastodonApi, @@ -56,23 +56,26 @@ class SearchViewModel @Inject constructor( private val loadedStatuses: MutableList = mutableListOf() - private val statusesPagingSourceFactory = SearchPagingSourceFactory(mastodonApi, SearchType.Status, loadedStatuses) { - it.statuses.map { status -> - status.toViewData( - isShowingContent = alwaysShowSensitiveMedia || !status.actionableStatus.sensitive, - isExpanded = alwaysOpenSpoiler, - isCollapsed = true - ) - }.apply { - loadedStatuses.addAll(this) + private val statusesPagingSourceFactory = + SearchPagingSourceFactory(mastodonApi, SearchType.Status, loadedStatuses) { + it.statuses.map { status -> + status.toViewData( + isShowingContent = alwaysShowSensitiveMedia || !status.actionableStatus.sensitive, + isExpanded = alwaysOpenSpoiler, + isCollapsed = true + ) + }.apply { + loadedStatuses.addAll(this) + } + } + private val accountsPagingSourceFactory = + SearchPagingSourceFactory(mastodonApi, SearchType.Account) { + it.accounts + } + private val hashtagsPagingSourceFactory = + SearchPagingSourceFactory(mastodonApi, SearchType.Hashtag) { + it.hashtags } - } - private val accountsPagingSourceFactory = SearchPagingSourceFactory(mastodonApi, SearchType.Account) { - it.accounts - } - private val hashtagsPagingSourceFactory = SearchPagingSourceFactory(mastodonApi, SearchType.Hashtag) { - it.hashtags - } val statusesFlow = Pager( config = PagingConfig(pageSize = DEFAULT_LOAD_SIZE, initialLoadSize = DEFAULT_LOAD_SIZE), diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt index a8b913081..6efdcb364 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt @@ -48,11 +48,15 @@ class SearchAccountsAdapter(private val linkListener: LinkListener, private val companion object { val ACCOUNT_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: TimelineAccount, newItem: TimelineAccount): Boolean = - oldItem == newItem + override fun areContentsTheSame( + oldItem: TimelineAccount, + newItem: TimelineAccount + ): Boolean = oldItem == newItem - override fun areItemsTheSame(oldItem: TimelineAccount, newItem: TimelineAccount): Boolean = - oldItem.id == newItem.id + override fun areItemsTheSame( + oldItem: TimelineAccount, + newItem: TimelineAccount + ): Boolean = oldItem.id == newItem.id } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt index 50bd0f933..052ccc9b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt @@ -27,7 +27,10 @@ import com.keylesspalace.tusky.util.BindingHolder class SearchHashtagsAdapter(private val linkListener: LinkListener) : PagingDataAdapter>(HASHTAG_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): BindingHolder { val binding = ItemHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false) return BindingHolder(binding) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt index a7c4c90eb..1d3cabe21 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt @@ -45,11 +45,15 @@ class SearchStatusesAdapter( companion object { val STATUS_COMPARATOR = object : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean = - oldItem == newItem + override fun areContentsTheSame( + oldItem: StatusViewData.Concrete, + newItem: StatusViewData.Concrete + ): Boolean = oldItem == newItem - override fun areItemsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean = - oldItem.id == newItem.id + override fun areItemsTheSame( + oldItem: StatusViewData.Concrete, + newItem: StatusViewData.Concrete + ): Boolean = oldItem.id == newItem.id } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt index 1bcfaaaa0..8e1c8100f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchAccountsFragment.kt @@ -38,7 +38,9 @@ class SearchAccountsFragment : SearchFragment() { } override fun createAdapter(): PagingDataAdapter { - val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context) + val preferences = PreferenceManager.getDefaultSharedPreferences( + binding.searchRecyclerView.context + ) return SearchAccountsAdapter( this, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt index 48a9c38aa..5a59c790b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt @@ -34,10 +34,10 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject abstract class SearchFragment : Fragment(R.layout.fragment_search), @@ -92,8 +92,12 @@ abstract class SearchFragment : val isNewSearch = currentQuery != viewModel.currentQuery - binding.searchProgressBar.visible(loadState.refresh == LoadState.Loading && isNewSearch && !binding.swipeRefreshLayout.isRefreshing) - binding.searchRecyclerView.visible(loadState.refresh is LoadState.NotLoading || !isNewSearch || binding.swipeRefreshLayout.isRefreshing) + binding.searchProgressBar.visible( + loadState.refresh == LoadState.Loading && isNewSearch && !binding.swipeRefreshLayout.isRefreshing + ) + binding.searchRecyclerView.visible( + loadState.refresh is LoadState.NotLoading || !isNewSearch || binding.swipeRefreshLayout.isRefreshing + ) if (loadState.refresh != LoadState.Loading) { binding.swipeRefreshLayout.isRefreshing = false @@ -102,7 +106,9 @@ abstract class SearchFragment : binding.progressBarBottom.visible(loadState.append == LoadState.Loading) - binding.searchNoResultsText.visible(loadState.refresh is LoadState.NotLoading && adapter.itemCount == 0 && viewModel.currentQuery.isNotEmpty()) + binding.searchNoResultsText.visible( + loadState.refresh is LoadState.NotLoading && adapter.itemCount == 0 && viewModel.currentQuery.isNotEmpty() + ) } } @@ -147,11 +153,15 @@ abstract class SearchFragment : } override fun onViewAccount(id: String) { - bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id)) + bottomSheetActivity?.startActivityWithSlideInAnimation( + AccountActivity.getIntent(requireContext(), id) + ) } override fun onViewTag(tag: String) { - bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) + bottomSheetActivity?.startActivityWithSlideInAnimation( + StatusListActivity.newHashtagIntent(requireContext(), tag) + ) } override fun onViewUrl(url: String) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index b7c7261c1..d0f14c771 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -61,9 +61,9 @@ import com.keylesspalace.tusky.util.openLink import com.keylesspalace.tusky.view.showMuteAccountDialog import com.keylesspalace.tusky.viewdata.AttachmentViewData import com.keylesspalace.tusky.viewdata.StatusViewData +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import javax.inject.Inject class SearchStatusesFragment : SearchFragment(), StatusActionListener { @Inject @@ -76,7 +76,9 @@ class SearchStatusesFragment : SearchFragment(), Status get() = super.adapter as SearchStatusesAdapter override fun createAdapter(): PagingDataAdapter { - val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context) + val preferences = PreferenceManager.getDefaultSharedPreferences( + binding.searchRecyclerView.context + ) val statusDisplayOptions = StatusDisplayOptions( animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false), mediaPreviewEnabled = viewModel.mediaPreviewEnabled, @@ -93,7 +95,12 @@ class SearchStatusesFragment : SearchFragment(), Status openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler ) - binding.searchRecyclerView.addItemDecoration(DividerItemDecoration(binding.searchRecyclerView.context, DividerItemDecoration.VERTICAL)) + binding.searchRecyclerView.addItemDecoration( + DividerItemDecoration( + binding.searchRecyclerView.context, + DividerItemDecoration.VERTICAL + ) + ) binding.searchRecyclerView.layoutManager = LinearLayoutManager(binding.searchRecyclerView.context) return SearchStatusesAdapter(statusDisplayOptions, this) } @@ -252,7 +259,10 @@ class SearchStatusesFragment : SearchFragment(), Status menu.findItem(R.id.status_open_as).isVisible = !statusUrl.isNullOrBlank() when (status.visibility) { Status.Visibility.PUBLIC, Status.Visibility.UNLISTED -> { - val textId = getString(if (status.isPinned()) R.string.unpin_action else R.string.pin_action) + val textId = + getString( + if (status.isPinned()) R.string.unpin_action else R.string.pin_action + ) menu.add(0, R.id.pin, 1, textId) } Status.Visibility.PRIVATE -> { @@ -305,7 +315,12 @@ class SearchStatusesFragment : SearchFragment(), Status statusToShare.content sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare) sendIntent.type = "text/plain" - startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_post_content_to))) + startActivity( + Intent.createChooser( + sendIntent, + resources.getText(R.string.send_post_content_to) + ) + ) return@setOnMenuItemClickListener true } R.id.post_share_link -> { @@ -313,11 +328,18 @@ class SearchStatusesFragment : SearchFragment(), Status sendIntent.action = Intent.ACTION_SEND sendIntent.putExtra(Intent.EXTRA_TEXT, statusUrl) sendIntent.type = "text/plain" - startActivity(Intent.createChooser(sendIntent, resources.getText(R.string.send_post_link_to))) + startActivity( + Intent.createChooser( + sendIntent, + resources.getText(R.string.send_post_link_to) + ) + ) return@setOnMenuItemClickListener true } R.id.status_copy_link -> { - val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = requireActivity().getSystemService( + Context.CLIPBOARD_SERVICE + ) as ClipboardManager clipboard.setPrimaryClip(ClipData.newPlainText(null, statusUrl)) return@setOnMenuItemClickListener true } @@ -418,7 +440,9 @@ class SearchStatusesFragment : SearchFragment(), Status val uri = Uri.parse(url) val filename = uri.lastPathSegment - val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val downloadManager = requireActivity().getSystemService( + Context.DOWNLOAD_SERVICE + ) as DownloadManager val request = DownloadManager.Request(uri) request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename) downloadManager.enqueue(request) @@ -445,7 +469,9 @@ class SearchStatusesFragment : SearchFragment(), Status } private fun openReportPage(accountId: String, accountUsername: String, statusId: String) { - startActivity(ReportActivity.getIntent(requireContext(), accountId, accountUsername, statusId)) + startActivity( + ReportActivity.getIntent(requireContext(), accountId, accountUsername, statusId) + ) } private fun showConfirmDeleteDialog(id: String, position: Int) { @@ -495,7 +521,11 @@ class SearchStatusesFragment : SearchFragment(), Status }, { error -> Log.w("SearchStatusesFragment", "error deleting status", error) - Toast.makeText(context, R.string.error_generic, Toast.LENGTH_SHORT).show() + Toast.makeText( + context, + R.string.error_generic, + Toast.LENGTH_SHORT + ).show() } ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt index 40f8b7d2b..b817ab59e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt @@ -76,10 +76,10 @@ import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch class TimelineFragment : SFragment(), @@ -232,7 +232,10 @@ class TimelineFragment : is LoadState.NotLoading -> { if (loadState.append is LoadState.NotLoading && loadState.source.refresh is LoadState.NotLoading) { binding.statusView.show() - binding.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty) + binding.statusView.setup( + R.drawable.elephant_friend_empty, + R.string.message_empty + ) if (kind == TimelineViewModel.Kind.HOME) { binding.statusView.showHelp(R.string.help_empty_home) } @@ -240,7 +243,9 @@ class TimelineFragment : } is LoadState.Error -> { binding.statusView.show() - binding.statusView.setup((loadState.refresh as LoadState.Error).error) { onRefresh() } + binding.statusView.setup( + (loadState.refresh as LoadState.Error).error + ) { onRefresh() } } is LoadState.Loading -> { binding.progressBar.show() @@ -255,7 +260,10 @@ class TimelineFragment : binding.recyclerView.post { if (getView() != null) { if (isSwipeToRefreshEnabled) { - binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30)) + binding.recyclerView.scrollBy( + 0, + Utils.dpToPx(requireContext(), -30) + ) } else { binding.recyclerView.scrollToPosition(0) } @@ -352,7 +360,11 @@ class TimelineFragment : val statusIdBelowLoadMore = statusIdBelowLoadMore ?: return var status: StatusViewData? - while (adapter.peek(position).let { status = it; it != null }) { + while (adapter.peek(position).let { + status = it + it != null + } + ) { if (status?.id == statusIdBelowLoadMore) { val lastVisiblePosition = (binding.recyclerView.layoutManager as LinearLayoutManager).findLastVisibleItemPosition() @@ -498,9 +510,9 @@ class TimelineFragment : override fun onViewAccount(id: String) { if (( - viewModel.kind == TimelineViewModel.Kind.USER || - viewModel.kind == TimelineViewModel.Kind.USER_WITH_REPLIES - ) && + viewModel.kind == TimelineViewModel.Kind.USER || + viewModel.kind == TimelineViewModel.Kind.USER_WITH_REPLIES + ) && viewModel.id == id ) { /* If already viewing an account page, then any requests to view that account page @@ -602,7 +614,11 @@ class TimelineFragment : .observeOn(AndroidSchedulers.mainThread()) .autoDispose(this, Lifecycle.Event.ON_PAUSE) .subscribe { - adapter.notifyItemRangeChanged(0, adapter.itemCount, listOf(StatusBaseViewHolder.Key.KEY_CREATED)) + adapter.notifyItemRangeChanged( + 0, + adapter.itemCount, + listOf(StatusBaseViewHolder.Key.KEY_CREATED) + ) } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelinePagingAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelinePagingAdapter.kt index a232989b3..716a30199 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelinePagingAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelinePagingAdapter.kt @@ -53,7 +53,9 @@ class TimelinePagingAdapter( StatusViewHolder(inflater.inflate(R.layout.item_status_wrapper, viewGroup, false)) } VIEW_TYPE_PLACEHOLDER -> { - PlaceholderViewHolder(inflater.inflate(R.layout.item_status_placeholder, viewGroup, false)) + PlaceholderViewHolder( + inflater.inflate(R.layout.item_status_placeholder, viewGroup, false) + ) } else -> { StatusViewHolder(inflater.inflate(R.layout.item_status, viewGroup, false)) @@ -124,10 +126,7 @@ class TimelinePagingAdapter( return false // Items are different always. It allows to refresh timestamp on every view holder update } - override fun getChangePayload( - oldItem: StatusViewData, - newItem: StatusViewData - ): Any? { + override fun getChangePayload(oldItem: StatusViewData, newItem: StatusViewData): Any? { return if (oldItem == newItem) { // If items are equal - update timestamp only listOf(StatusBaseViewHolder.Key.KEY_CREATED) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt index bdb3d64d7..b09a59aee 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt @@ -205,13 +205,15 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false val status = if (reblog != null) { Status( id = status.serverId, - url = null, // no url for reblogs + // no url for reblogs + url = null, account = this.reblogAccount!!.toAccount(gson), inReplyToId = null, inReplyToAccountId = null, reblog = reblog, content = "", - createdAt = Date(status.createdAt), // lie but whatever? + // lie but whatever? + createdAt = Date(status.createdAt), editedAt = null, emojis = listOf(), reblogsCount = 0, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt index 4a1d75f92..617df17a1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/util/TimelineUtils.kt @@ -1,15 +1,13 @@ package com.keylesspalace.tusky.components.timeline.util import com.google.gson.JsonParseException -import retrofit2.HttpException import java.io.IOException +import retrofit2.HttpException -fun Throwable.isExpected() = this is IOException || this is HttpException || this is JsonParseException +fun Throwable.isExpected() = + this is IOException || this is HttpException || this is JsonParseException -inline fun ifExpected( - t: Throwable, - cb: () -> T -): T { +inline fun ifExpected(t: Throwable, cb: () -> T): T { if (t.isExpected()) { return cb() } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt index 2746b1acf..49b77f4c8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineRemoteMediator.kt @@ -68,7 +68,8 @@ class CachedTimelineRemoteMediator( topId?.let { cachedTopId -> val statusResponse = api.homeTimeline( maxId = cachedTopId, - sinceId = topPlaceholderId, // so already existing placeholders don't get accidentally overwritten + // so already existing placeholders don't get accidentally overwritten + sinceId = topPlaceholderId, limit = state.config.pageSize ) @@ -131,7 +132,10 @@ class CachedTimelineRemoteMediator( * @param statuses the new statuses * @return the number of old statuses that have been cleared from the database */ - private suspend fun replaceStatusRange(statuses: List, state: PagingState): Int { + private suspend fun replaceStatusRange( + statuses: List, + state: PagingState + ): Int { val overlappedStatuses = if (statuses.isNotEmpty()) { timelineDao.deleteRange(activeAccount.id, statuses.last().id, statuses.first().id) } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt index 4ad77fd0b..331af2702 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/CachedTimelineViewModel.kt @@ -45,13 +45,13 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.usecase.TimelineCases import com.keylesspalace.tusky.util.EmptyPagingSource import com.keylesspalace.tusky.viewdata.StatusViewData +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import retrofit2.HttpException -import javax.inject.Inject /** * TimelineViewModel that caches all statuses in a local database diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt index bb8c08c08..ebfebd58e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/NetworkTimelineViewModel.kt @@ -37,6 +37,8 @@ import com.keylesspalace.tusky.util.isLessThan import com.keylesspalace.tusky.util.isLessThanOrEqual import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.viewdata.StatusViewData +import java.io.IOException +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.flow.flowOn @@ -44,8 +46,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import retrofit2.HttpException import retrofit2.Response -import java.io.IOException -import javax.inject.Inject /** * TimelineViewModel that caches all statuses in an in-memory list @@ -57,7 +57,14 @@ class NetworkTimelineViewModel @Inject constructor( accountManager: AccountManager, sharedPreferences: SharedPreferences, filterModel: FilterModel -) : TimelineViewModel(timelineCases, api, eventHub, accountManager, sharedPreferences, filterModel) { +) : TimelineViewModel( + timelineCases, + api, + eventHub, + accountManager, + sharedPreferences, + filterModel +) { var currentSource: NetworkTimelinePagingSource? = null @@ -168,11 +175,17 @@ class NetworkTimelineViewModel @Inject constructor( if (statuses.isNotEmpty()) { val firstId = statuses.first().id val lastId = statuses.last().id - val overlappedFrom = statusData.indexOfFirst { it.asStatusOrNull()?.id?.isLessThanOrEqual(firstId) ?: false } + val overlappedFrom = statusData.indexOfFirst { + it.asStatusOrNull()?.id?.isLessThanOrEqual(firstId) ?: false + } val overlappedTo = statusData.indexOfFirst { it.asStatusOrNull()?.id?.isLessThan(lastId) ?: false } if (overlappedFrom < overlappedTo) { - data.mapIndexed { i, status -> i to statusData.firstOrNull { it.asStatusOrNull()?.id == status.id }?.asStatusOrNull() } + data.mapIndexed { i, status -> + i to statusData.firstOrNull { + it.asStatusOrNull()?.id == status.id + }?.asStatusOrNull() + } .filter { (_, oldStatus) -> oldStatus != null } .forEach { (i, oldStatus) -> data[i] = data[i].asStatusOrNull()!! @@ -309,10 +322,7 @@ class NetworkTimelineViewModel @Inject constructor( updateViewDataAt(pos, updater) } - private inline fun updateActionableStatusById( - id: String, - updater: (Status) -> Status - ) { + private inline fun updateActionableStatusById(id: String, updater: (Status) -> Status) { val pos = statusData.indexOfFirst { it.asStatusOrNull()?.id == id } if (pos == -1) return updateViewDataAt(pos) { vd -> diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt index 7ae866307..d27c6e6ba 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/viewmodel/TimelineViewModel.kt @@ -76,11 +76,7 @@ abstract class TimelineViewModel( private var filterRemoveSelfReblogs = false protected var readingOrder: ReadingOrder = ReadingOrder.OLDEST_FIRST - fun init( - kind: Kind, - id: String?, - tags: List - ) { + fun init(kind: Kind, id: String?, tags: List) { this.kind = kind this.id = id this.tags = tags @@ -138,23 +134,24 @@ abstract class TimelineViewModel( } } - fun voteInPoll(choices: List, status: StatusViewData.Concrete): Job = viewModelScope.launch { - val poll = status.status.actionableStatus.poll ?: run { - Log.w(TAG, "No poll on status ${status.id}") - return@launch - } + fun voteInPoll(choices: List, status: StatusViewData.Concrete): Job = + viewModelScope.launch { + val poll = status.status.actionableStatus.poll ?: run { + Log.w(TAG, "No poll on status ${status.id}") + return@launch + } - val votedPoll = poll.votedCopy(choices) - updatePoll(votedPoll, status) + val votedPoll = poll.votedCopy(choices) + updatePoll(votedPoll, status) - try { - timelineCases.voteInPoll(status.actionableId, poll.id, choices).getOrThrow() - } catch (t: Exception) { - ifExpected(t) { - Log.d(TAG, "Failed to vote in poll: " + status.actionableId, t) + try { + timelineCases.voteInPoll(status.actionableId, poll.id, choices).getOrThrow() + } catch (t: Exception) { + ifExpected(t) { + Log.d(TAG, "Failed to vote in poll: " + status.actionableId, t) + } } } - } abstract fun updatePoll(newPoll: Poll, status: StatusViewData.Concrete) @@ -319,16 +316,23 @@ abstract class TimelineViewModel( private const val TAG = "TimelineVM" internal const val LOAD_AT_ONCE = 30 - fun filterContextMatchesKind( - kind: Kind, - filterContext: List - ): Boolean { + fun filterContextMatchesKind(kind: Kind, filterContext: List): Boolean { return filterContext.contains(kind.toFilterKind().kind) } } enum class Kind { - HOME, PUBLIC_LOCAL, PUBLIC_FEDERATED, TAG, USER, USER_PINNED, USER_WITH_REPLIES, FAVOURITES, LIST, BOOKMARKS, PUBLIC_TRENDING_STATUSES; + HOME, + PUBLIC_LOCAL, + PUBLIC_FEDERATED, + TAG, + USER, + USER_PINNED, + USER_WITH_REPLIES, + FAVOURITES, + LIST, + BOOKMARKS, + PUBLIC_TRENDING_STATUSES; fun toFilterKind(): Filter.Kind { return when (valueOf(name)) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt index 58aad9e84..ac1e8c72d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagViewHolder.kt @@ -25,10 +25,7 @@ class TrendingTagViewHolder( private val binding: ItemTrendingCellBinding ) : RecyclerView.ViewHolder(binding.root) { - fun setup( - tagViewData: TrendingViewData.Tag, - onViewTag: (String) -> Unit - ) { + fun setup(tagViewData: TrendingViewData.Tag, onViewTag: (String) -> Unit) { binding.tag.text = binding.root.context.getString(R.string.title_tag, tagViewData.name) binding.graph.maxTrendingValue = tagViewData.maxTrendingValue diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt index c04caee6d..ba5e7821f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt @@ -44,9 +44,9 @@ import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewdata.TrendingViewData +import javax.inject.Inject import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import javax.inject.Inject class TrendingTagsFragment : Fragment(R.layout.fragment_trending_tags), @@ -136,7 +136,9 @@ class TrendingTagsFragment : } fun onViewTag(tag: String) { - (requireActivity() as BaseActivity).startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) + (requireActivity() as BaseActivity).startActivityWithSlideInAnimation( + StatusListActivity.newHashtagIntent(requireContext(), tag) + ) } private fun processViewState(uiState: TrendingTagsViewModel.TrendingTagsUiState) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingTagsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingTagsViewModel.kt index 92f0a8c46..5a7ad9232 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingTagsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/trending/viewmodel/TrendingTagsViewModel.kt @@ -27,20 +27,25 @@ import com.keylesspalace.tusky.entity.start import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.viewdata.TrendingViewData +import java.io.IOException +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch -import java.io.IOException -import javax.inject.Inject class TrendingTagsViewModel @Inject constructor( private val mastodonApi: MastodonApi, private val eventHub: EventHub ) : ViewModel() { enum class LoadingState { - INITIAL, LOADING, REFRESHING, LOADED, ERROR_NETWORK, ERROR_OTHER + INITIAL, + LOADING, + REFRESHING, + LOADED, + ERROR_NETWORK, + ERROR_OTHER } data class TrendingTagsUiState( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ConversationLineItemDecoration.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ConversationLineItemDecoration.kt index ffd1b9c83..520445fac 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ConversationLineItemDecoration.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ConversationLineItemDecoration.kt @@ -25,11 +25,18 @@ import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.R class ConversationLineItemDecoration(context: Context) : RecyclerView.ItemDecoration() { - private val divider: Drawable = ContextCompat.getDrawable(context, R.drawable.conversation_thread_line)!! + private val divider: Drawable = ContextCompat.getDrawable( + context, + R.drawable.conversation_thread_line + )!! - private val avatarTopMargin = context.resources.getDimensionPixelSize(R.dimen.account_avatar_margin) + private val avatarTopMargin = context.resources.getDimensionPixelSize( + R.dimen.account_avatar_margin + ) private val halfAvatarHeight = context.resources.getDimensionPixelSize(R.dimen.timeline_status_avatar_height) / 2 - private val statusLineMarginStart = context.resources.getDimensionPixelSize(R.dimen.status_line_margin_start) + private val statusLineMarginStart = context.resources.getDimensionPixelSize( + R.dimen.status_line_margin_start + ) override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { val dividerStart = parent.paddingStart + statusLineMarginStart @@ -57,7 +64,12 @@ class ConversationLineItemDecoration(context: Context) : RecyclerView.ItemDecora if (parent.layoutDirection == View.LAYOUT_DIRECTION_LTR) { divider.setBounds(dividerStart, dividerTop, dividerEnd, dividerBottom) } else { - divider.setBounds(canvas.width - dividerEnd, dividerTop, canvas.width - dividerStart, dividerBottom) + divider.setBounds( + canvas.width - dividerEnd, + dividerTop, + canvas.width - dividerStart, + dividerBottom + ) } divider.draw(canvas) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ThreadAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ThreadAdapter.kt index 1edffcaf9..0c1c7fc5b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ThreadAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ThreadAdapter.kt @@ -43,7 +43,9 @@ class ThreadAdapter( StatusViewHolder(inflater.inflate(R.layout.item_status_wrapper, parent, false)) } VIEW_TYPE_STATUS_DETAILED -> { - StatusDetailedViewHolder(inflater.inflate(R.layout.item_status_detailed, parent, false)) + StatusDetailedViewHolder( + inflater.inflate(R.layout.item_status_detailed, parent, false) + ) } else -> error("Unknown item type: $viewType") } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt index 4c1ee6d62..d4fd0c8b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt @@ -56,11 +56,11 @@ import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewdata.AttachmentViewData.Companion.list import com.keylesspalace.tusky.viewdata.StatusViewData +import javax.inject.Inject import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import javax.inject.Inject class ViewThreadFragment : SFragment(), @@ -200,7 +200,9 @@ class ViewThreadFragment : binding.recyclerView.hide() binding.statusView.show() - binding.statusView.setup(uiState.throwable) { viewModel.retry(thisThreadsStatusId) } + binding.statusView.setup( + uiState.throwable + ) { viewModel.retry(thisThreadsStatusId) } } is ThreadUiState.Success -> { if (uiState.statusViewData.none { viewData -> viewData.isDetailed }) { @@ -295,17 +297,18 @@ class ViewThreadFragment : * any time `view` is hidden. */ @CheckResult - private fun getProgressBarJob(view: View, delayMs: Long) = viewLifecycleOwner.lifecycleScope.launch( - start = CoroutineStart.LAZY - ) { - try { - delay(delayMs) - view.show() - awaitCancellation() - } finally { - view.hide() + private fun getProgressBarJob(view: View, delayMs: Long) = + viewLifecycleOwner.lifecycleScope.launch( + start = CoroutineStart.LAZY + ) { + try { + delay(delayMs) + view.show() + awaitCancellation() + } finally { + view.hide() + } } - } override fun onRefresh() { viewModel.refresh(thisThreadsStatusId) @@ -425,7 +428,12 @@ class ViewThreadFragment : val viewEditsFragment = ViewEditsFragment.newInstance(status.actionableId) parentFragmentManager.commit { - setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left, R.anim.slide_from_left, R.anim.slide_to_right) + setCustomAnimations( + R.anim.slide_from_right, + R.anim.slide_to_left, + R.anim.slide_from_left, + R.anim.slide_to_right + ) replace(R.id.fragment_container, viewEditsFragment, "ViewEditsFragment_$id") addToBackStack(null) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt index f33bf0792..a5607f354 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModel.kt @@ -40,6 +40,7 @@ import com.keylesspalace.tusky.usecase.TimelineCases import com.keylesspalace.tusky.util.isHttpNotFound import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.viewdata.StatusViewData +import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.channels.BufferOverflow @@ -48,7 +49,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import javax.inject.Inject class ViewThreadViewModel @Inject constructor( private val api: MastodonApi, @@ -64,7 +64,12 @@ class ViewThreadViewModel @Inject constructor( val uiState: Flow get() = _uiState - private val _errors = MutableSharedFlow(replay = 0, extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _errors = + MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val errors: Flow get() = _errors @@ -219,25 +224,26 @@ class ViewThreadViewModel @Inject constructor( } } - fun voteInPoll(choices: List, status: StatusViewData.Concrete): Job = viewModelScope.launch { - val poll = status.status.actionableStatus.poll ?: run { - Log.w(TAG, "No poll on status ${status.id}") - return@launch - } + fun voteInPoll(choices: List, status: StatusViewData.Concrete): Job = + viewModelScope.launch { + val poll = status.status.actionableStatus.poll ?: run { + Log.w(TAG, "No poll on status ${status.id}") + return@launch + } - val votedPoll = poll.votedCopy(choices) - updateStatus(status.id) { status -> - status.copy(poll = votedPoll) - } + val votedPoll = poll.votedCopy(choices) + updateStatus(status.id) { status -> + status.copy(poll = votedPoll) + } - try { - timelineCases.voteInPoll(status.actionableId, poll.id, choices).getOrThrow() - } catch (t: Exception) { - ifExpected(t) { - Log.d(TAG, "Failed to vote in poll: " + status.actionableId, t) + try { + timelineCases.voteInPoll(status.actionableId, poll.id, choices).getOrThrow() + } catch (t: Exception) { + ifExpected(t) { + Log.d(TAG, "Failed to vote in poll: " + status.actionableId, t) + } } } - } fun removeStatus(statusToRemove: StatusViewData.Concrete) { updateSuccess { uiState -> @@ -430,10 +436,10 @@ class ViewThreadViewModel @Inject constructor( } } - private fun Status.toViewData( - isDetailed: Boolean = false - ): StatusViewData.Concrete { - val oldStatus = (_uiState.value as? ThreadUiState.Success)?.statusViewData?.find { it.id == this.id } + private fun Status.toViewData(isDetailed: Boolean = false): StatusViewData.Concrete { + val oldStatus = (_uiState.value as? ThreadUiState.Success)?.statusViewData?.find { + it.id == this.id + } return toViewData( isShowingContent = oldStatus?.isShowingContent ?: (alwaysShowSensitiveMedia || !actionableStatus.sensitive), isExpanded = oldStatus?.isExpanded ?: alwaysOpenSpoiler, @@ -452,7 +458,10 @@ class ViewThreadViewModel @Inject constructor( } } - private fun updateStatusViewData(statusId: String, updater: (StatusViewData.Concrete) -> StatusViewData.Concrete) { + private fun updateStatusViewData( + statusId: String, + updater: (StatusViewData.Concrete) -> StatusViewData.Concrete + ) { updateSuccess { uiState -> uiState.copy( statusViewData = uiState.statusViewData.map { viewData -> @@ -510,5 +519,7 @@ sealed interface ThreadUiState { } enum class RevealButtonState { - NO_BUTTON, REVEAL, HIDE + NO_BUTTON, + REVEAL, + HIDE } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsAdapter.kt index 3866bde59..f1b0df430 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsAdapter.kt @@ -58,7 +58,11 @@ class ViewEditsAdapter( parent: ViewGroup, viewType: Int ): BindingHolder { - val binding = ItemStatusEditBinding.inflate(LayoutInflater.from(parent.context), parent, false) + val binding = ItemStatusEditBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) binding.statusEditMediaPreview.clipToOutline = true @@ -93,7 +97,10 @@ class ViewEditsAdapter( } else { largeTextSizePx } - binding.statusEditContentWarningDescription.setTextSize(TypedValue.COMPLEX_UNIT_PX, variableTextSize) + binding.statusEditContentWarningDescription.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + variableTextSize + ) binding.statusEditContent.setTextSize(TypedValue.COMPLEX_UNIT_PX, variableTextSize) binding.statusEditMediaSensitivity.setTextSize(TypedValue.COMPLEX_UNIT_PX, variableTextSize) @@ -119,7 +126,13 @@ class ViewEditsAdapter( .parseAsMastodonHtml(EditsTagHandler(context)) .emojify(edit.emojis, binding.statusEditContent, animateEmojis) - setClickableText(binding.statusEditContent, emojifiedText, emptyList(), emptyList(), listener) + setClickableText( + binding.statusEditContent, + emojifiedText, + emptyList(), + emptyList(), + listener + ) if (edit.poll == null) { binding.statusEditPollOptions.hide() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt index 95a0b96d1..9114fae7b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt @@ -52,8 +52,8 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class ViewEditsFragment : Fragment(R.layout.fragment_view_edits), @@ -90,7 +90,9 @@ class ViewEditsFragment : val animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false) val animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false) val useBlurhash = preferences.getBoolean(PrefKeys.USE_BLURHASH, true) - val avatarRadius: Int = requireContext().resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp) + val avatarRadius: Int = requireContext().resources.getDimensionPixelSize( + R.dimen.avatar_radius_48dp + ) viewLifecycleOwner.lifecycleScope.launch { viewModel.uiState.collect { uiState -> @@ -139,10 +141,17 @@ class ViewEditsFragment : ) // Focus on the most recent version - (binding.recyclerView.layoutManager as LinearLayoutManager).scrollToPosition(0) + (binding.recyclerView.layoutManager as LinearLayoutManager).scrollToPosition( + 0 + ) val account = uiState.edits.first().account - loadAvatar(account.avatar, binding.statusAvatar, avatarRadius, animateAvatars) + loadAvatar( + account.avatar, + binding.statusAvatar, + avatarRadius, + animateAvatars + ) binding.statusDisplayName.text = account.name.unicodeWrap().emojify(account.emojis, binding.statusDisplayName, animateEmojis) binding.statusUsername.text = account.username @@ -185,11 +194,15 @@ class ViewEditsFragment : } override fun onViewAccount(id: String) { - bottomSheetActivity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id)) + bottomSheetActivity?.startActivityWithSlideInAnimation( + AccountActivity.getIntent(requireContext(), id) + ) } override fun onViewTag(tag: String) { - bottomSheetActivity?.startActivityWithSlideInAnimation(StatusListActivity.newHashtagIntent(requireContext(), tag)) + bottomSheetActivity?.startActivityWithSlideInAnimation( + StatusListActivity.newHashtagIntent(requireContext(), tag) + ) } override fun onViewUrl(url: String) { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt index d1f0bcae4..5f5258c83 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsViewModel.kt @@ -22,6 +22,7 @@ import com.keylesspalace.tusky.components.viewthread.edits.EditsTagHandler.Compa import com.keylesspalace.tusky.components.viewthread.edits.EditsTagHandler.Companion.INSERTED_TEXT_EL import com.keylesspalace.tusky.entity.StatusEdit import com.keylesspalace.tusky.network.MastodonApi +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -41,7 +42,6 @@ import org.pageseeder.diffx.token.impl.SpaceToken import org.pageseeder.diffx.xml.NamespaceSet import org.pageseeder.xmlwriter.XML.NamespaceAware import org.pageseeder.xmlwriter.XMLStringWriter -import javax.inject.Inject class ViewEditsViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() { diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt index 1f88d3a10..40098593c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt @@ -38,8 +38,10 @@ data class AccountEntity( @field:PrimaryKey(autoGenerate = true) var id: Long, val domain: String, var accessToken: String, - var clientId: String?, // nullable for backward compatibility - var clientSecret: String?, // nullable for backward compatibility + // nullable for backward compatibility + var clientId: String?, + // nullable for backward compatibility + var clientSecret: String?, var isActive: Boolean, var accountId: String = "", var username: String = "", @@ -111,7 +113,7 @@ data class AccountEntity( var isShowHomeBoosts: Boolean = true, var isShowHomeReplies: Boolean = true, - var isShowHomeSelfBoosts: Boolean = true, + var isShowHomeSelfBoosts: Boolean = true ) { val identifier: String diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt index 9c7999fd9..d7397ea4e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt @@ -236,7 +236,10 @@ class AccountManager @Inject constructor(db: AppDatabase) { */ fun shouldDisplaySelfUsername(context: Context): Boolean { val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - val showUsernamePreference = sharedPreferences.getString(PrefKeys.SHOW_SELF_USERNAME, "disambiguate") + val showUsernamePreference = sharedPreferences.getString( + PrefKeys.SHOW_SELF_USERNAME, + "disambiguate" + ) if (showUsernamePreference == "always") { return true } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt index 491cd53d8..c999c8210 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt @@ -66,14 +66,19 @@ class Converters @Inject constructor( return str?.split(";") ?.map { val data = it.split(":") - createTabDataFromId(data[0], data.drop(1).map { s -> URLDecoder.decode(s, "UTF-8") }) + createTabDataFromId( + data[0], + data.drop(1).map { s -> URLDecoder.decode(s, "UTF-8") } + ) } } @TypeConverter fun tabDataToString(tabData: List?): String? { // List name may include ":" - return tabData?.joinToString(";") { it.id + ":" + it.arguments.joinToString(":") { s -> URLEncoder.encode(s, "UTF-8") } } + return tabData?.joinToString(";") { + it.id + ":" + it.arguments.joinToString(":") { s -> URLEncoder.encode(s, "UTF-8") } + } } @TypeConverter @@ -93,7 +98,10 @@ class Converters @Inject constructor( @TypeConverter fun jsonToAccountList(accountListJson: String?): List? { - return gson.fromJson(accountListJson, object : TypeToken>() {}.type) + return gson.fromJson( + accountListJson, + object : TypeToken>() {}.type + ) } @TypeConverter @@ -163,7 +171,10 @@ class Converters @Inject constructor( @TypeConverter fun jsonToDraftAttachmentList(draftAttachmentListJson: String?): List? { - return gson.fromJson(draftAttachmentListJson, object : TypeToken>() {}.type) + return gson.fromJson( + draftAttachmentListJson, + object : TypeToken>() {}.type + ) } @TypeConverter diff --git a/app/src/main/java/com/keylesspalace/tusky/db/DraftDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/DraftDao.kt index 7b1f62b8c..4df024469 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/DraftDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/DraftDao.kt @@ -34,7 +34,9 @@ interface DraftDao { @Query("SELECT COUNT(*) FROM DraftEntity WHERE accountId = :accountId AND failedToSendNew = 1") fun draftsNeedUserAlert(accountId: Long): LiveData - @Query("UPDATE DraftEntity SET failedToSendNew = 0 WHERE accountId = :accountId AND failedToSendNew = 1") + @Query( + "UPDATE DraftEntity SET failedToSendNew = 0 WHERE accountId = :accountId AND failedToSendNew = 1" + ) suspend fun draftsClearNeedUserAlert(accountId: Long) @Query("SELECT * FROM DraftEntity WHERE accountId = :accountId") diff --git a/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt index 2a479ab34..3dacaef49 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/DraftEntity.kt @@ -62,6 +62,8 @@ data class DraftAttachment( get() = uriString.toUri() enum class Type { - IMAGE, VIDEO, AUDIO; + IMAGE, + VIDEO, + AUDIO } } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/DraftsAlert.kt b/app/src/main/java/com/keylesspalace/tusky/db/DraftsAlert.kt index 9d11f47d3..9b9bcb7cd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/DraftsAlert.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/DraftsAlert.kt @@ -1,99 +1,99 @@ -/* Copyright 2023 Andi McClure - * - * This file is a part of Tusky. - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License along with Tusky; if not, - * see . */ - -package com.keylesspalace.tusky.db - -import android.content.Context -import android.content.DialogInterface -import android.util.Log -import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.LifecycleCoroutineScope -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.components.drafts.DraftsActivity -import kotlinx.coroutines.launch -import javax.inject.Inject -import javax.inject.Singleton - -/** - * This class manages an alert popup when a post has failed and been saved to drafts. - * It must be separately registered in each lifetime in which it is to appear, - * and it only appears if the post failure belongs to the current user. - */ - -private const val TAG = "DraftsAlert" - -@Singleton -class DraftsAlert @Inject constructor(db: AppDatabase) { - // For tracking when a media upload fails in the service - private val draftDao: DraftDao = db.draftDao() - - @Inject - lateinit var accountManager: AccountManager - - fun observeInContext(context: T, showAlert: Boolean) where T : Context, T : LifecycleOwner { - accountManager.activeAccount?.let { activeAccount -> - val coroutineScope = context.lifecycleScope - - // Assume a single MainActivity, AccountActivity or DraftsActivity never sees more then one user id in its lifetime. - val activeAccountId = activeAccount.id - - // This LiveData will be automatically disposed when the activity is destroyed. - val draftsNeedUserAlert = draftDao.draftsNeedUserAlert(activeAccountId) - - // observe ensures that this gets called at the most appropriate moment wrt the context lifecycle— - // at init, at next onResume, or immediately if the context is resumed already. - if (showAlert) { - draftsNeedUserAlert.observe(context) { count -> - Log.d(TAG, "User id $activeAccountId changed: Notification-worthy draft count $count") - if (count > 0) { - AlertDialog.Builder(context) - .setTitle(R.string.action_post_failed) - .setMessage( - context.resources.getQuantityString(R.plurals.action_post_failed_detail, count) - ) - .setPositiveButton(R.string.action_post_failed_show_drafts) { _: DialogInterface?, _: Int -> - clearDraftsAlert(coroutineScope, activeAccountId) // User looked at drafts - - val intent = DraftsActivity.newIntent(context) - context.startActivity(intent) - } - .setNegativeButton(R.string.action_post_failed_do_nothing) { _: DialogInterface?, _: Int -> - clearDraftsAlert(coroutineScope, activeAccountId) // User doesn't care - } - .show() - } - } - } else { - draftsNeedUserAlert.observe(context) { - Log.d(TAG, "User id $activeAccountId: Clean out notification-worthy drafts") - clearDraftsAlert(coroutineScope, activeAccountId) - } - } - } ?: run { - Log.w(TAG, "Attempted to observe drafts, but there is no active account") - } - } - - /** - * Clear drafts alert for specified user - */ - private fun clearDraftsAlert(coroutineScope: LifecycleCoroutineScope, id: Long) { - coroutineScope.launch { - draftDao.draftsClearNeedUserAlert(id) - } - } -} +/* Copyright 2023 Andi McClure + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . */ + +package com.keylesspalace.tusky.db + +import android.content.Context +import android.content.DialogInterface +import android.util.Log +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.LifecycleCoroutineScope +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.components.drafts.DraftsActivity +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.launch + +/** + * This class manages an alert popup when a post has failed and been saved to drafts. + * It must be separately registered in each lifetime in which it is to appear, + * and it only appears if the post failure belongs to the current user. + */ + +private const val TAG = "DraftsAlert" + +@Singleton +class DraftsAlert @Inject constructor(db: AppDatabase) { + // For tracking when a media upload fails in the service + private val draftDao: DraftDao = db.draftDao() + + @Inject + lateinit var accountManager: AccountManager + + fun observeInContext(context: T, showAlert: Boolean) where T : Context, T : LifecycleOwner { + accountManager.activeAccount?.let { activeAccount -> + val coroutineScope = context.lifecycleScope + + // Assume a single MainActivity, AccountActivity or DraftsActivity never sees more then one user id in its lifetime. + val activeAccountId = activeAccount.id + + // This LiveData will be automatically disposed when the activity is destroyed. + val draftsNeedUserAlert = draftDao.draftsNeedUserAlert(activeAccountId) + + // observe ensures that this gets called at the most appropriate moment wrt the context lifecycle— + // at init, at next onResume, or immediately if the context is resumed already. + if (showAlert) { + draftsNeedUserAlert.observe(context) { count -> + Log.d(TAG, "User id $activeAccountId changed: Notification-worthy draft count $count") + if (count > 0) { + AlertDialog.Builder(context) + .setTitle(R.string.action_post_failed) + .setMessage( + context.resources.getQuantityString(R.plurals.action_post_failed_detail, count) + ) + .setPositiveButton(R.string.action_post_failed_show_drafts) { _: DialogInterface?, _: Int -> + clearDraftsAlert(coroutineScope, activeAccountId) // User looked at drafts + + val intent = DraftsActivity.newIntent(context) + context.startActivity(intent) + } + .setNegativeButton(R.string.action_post_failed_do_nothing) { _: DialogInterface?, _: Int -> + clearDraftsAlert(coroutineScope, activeAccountId) // User doesn't care + } + .show() + } + } + } else { + draftsNeedUserAlert.observe(context) { + Log.d(TAG, "User id $activeAccountId: Clean out notification-worthy drafts") + clearDraftsAlert(coroutineScope, activeAccountId) + } + } + } ?: run { + Log.w(TAG, "Attempted to observe drafts, but there is no active account") + } + } + + /** + * Clear drafts alert for specified user + */ + private fun clearDraftsAlert(coroutineScope: LifecycleCoroutineScope, id: Long) { + coroutineScope.launch { + draftDao.draftsClearNeedUserAlert(id) + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index 2d9d0bac8..aa3d55b74 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -255,13 +255,21 @@ WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = """UPDATE TimelineStatusEntity SET contentShowing = :contentShowing WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract suspend fun setContentShowing(accountId: Long, statusId: String, contentShowing: Boolean) + abstract suspend fun setContentShowing( + accountId: Long, + statusId: String, + contentShowing: Boolean + ) @Query( """UPDATE TimelineStatusEntity SET contentCollapsed = :contentCollapsed WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)""" ) - abstract suspend fun setContentCollapsed(accountId: Long, statusId: String, contentCollapsed: Boolean) + abstract suspend fun setContentCollapsed( + accountId: Long, + statusId: String, + contentCollapsed: Boolean + ) @Query( """UPDATE TimelineStatusEntity SET pinned = :pinned @@ -278,39 +286,53 @@ AND timelineUserId = :accountId ) abstract suspend fun deleteAllFromInstance(accountId: Long, instanceDomain: String) - @Query("UPDATE TimelineStatusEntity SET filtered = NULL WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)") + @Query( + "UPDATE TimelineStatusEntity SET filtered = NULL WHERE timelineUserId = :accountId AND (serverId = :statusId OR reblogServerId = :statusId)" + ) abstract suspend fun clearWarning(accountId: Long, statusId: String): Int - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1" + ) abstract suspend fun getTopId(accountId: Long): String? - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND authorServerId IS NULL ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND authorServerId IS NULL ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1" + ) abstract suspend fun getTopPlaceholderId(accountId: Long): String? /** * Returns the id directly above [serverId], or null if [serverId] is the id of the top status */ - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND (LENGTH(:serverId) < LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId < serverId)) ORDER BY LENGTH(serverId) ASC, serverId ASC LIMIT 1") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND (LENGTH(:serverId) < LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId < serverId)) ORDER BY LENGTH(serverId) ASC, serverId ASC LIMIT 1" + ) abstract suspend fun getIdAbove(accountId: Long, serverId: String): String? /** * Returns the ID directly below [serverId], or null if [serverId] is the ID of the bottom * status */ - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND (LENGTH(:serverId) > LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId > serverId)) ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND (LENGTH(:serverId) > LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId > serverId)) ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1" + ) abstract suspend fun getIdBelow(accountId: Long, serverId: String): String? /** * Returns the id of the next placeholder after [serverId] */ - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND authorServerId IS NULL AND (LENGTH(:serverId) > LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId > serverId)) ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId AND authorServerId IS NULL AND (LENGTH(:serverId) > LENGTH(serverId) OR (LENGTH(:serverId) = LENGTH(serverId) AND :serverId > serverId)) ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT 1" + ) abstract suspend fun getNextPlaceholderIdAfter(accountId: Long, serverId: String): String? @Query("SELECT COUNT(*) FROM TimelineStatusEntity WHERE timelineUserId = :accountId") abstract suspend fun getStatusCount(accountId: Long): Int /** Developer tools: Find N most recent status IDs */ - @Query("SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT :count") + @Query( + "SELECT serverId FROM TimelineStatusEntity WHERE timelineUserId = :accountId ORDER BY LENGTH(serverId) DESC, serverId DESC LIMIT :count" + ) abstract suspend fun getMostRecentNStatusIds(accountId: Long, count: Int): List /** Developer tools: Convert a status to a placeholder */ diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt index f0f7f98ed..ba32f6380 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt @@ -50,7 +50,8 @@ import com.keylesspalace.tusky.entity.Status ) @TypeConverters(Converters::class) data class TimelineStatusEntity( - val serverId: String, // id never flips: we need it for sorting so it's a real id + // id never flips: we need it for sorting so it's a real id + val serverId: String, val url: String?, // our local id for the logged in user in case there are multiple accounts per instance val timelineUserId: Long, @@ -74,7 +75,8 @@ data class TimelineStatusEntity( val mentions: String?, val tags: String?, val application: String?, - val reblogServerId: String?, // if it has a reblogged status, it's id is stored here + // if it has a reblogged status, it's id is stored here + val reblogServerId: String?, val reblogAccountId: String?, val poll: String?, val muted: Boolean?, @@ -109,8 +111,10 @@ data class TimelineAccountEntity( data class TimelineStatusWithAccount( @Embedded val status: TimelineStatusEntity, + // null when placeholder @Embedded(prefix = "a_") - val account: TimelineAccountEntity? = null, // null when placeholder + val account: TimelineAccountEntity? = null, + // null when no reblog @Embedded(prefix = "rb_") - val reblogAccount: TimelineAccountEntity? = null // null when no reblog + val reblogAccount: TimelineAccountEntity? = null ) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt index 6446a7357..fa6fae851 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/AppInjector.kt @@ -68,7 +68,11 @@ object AppInjector { if (activity is FragmentActivity) { activity.supportFragmentManager.registerFragmentLifecycleCallbacks( object : FragmentManager.FragmentLifecycleCallbacks() { - override fun onFragmentPreAttached(fm: FragmentManager, f: Fragment, context: Context) { + override fun onFragmentPreAttached( + fm: FragmentManager, + f: Fragment, + context: Context + ) { if (f is Injectable) { AndroidSupportInjection.inject(f) } diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt index 95284f1ad..0f9277dda 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt @@ -68,7 +68,7 @@ class AppModule { AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41, AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44, AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47, - AppDatabase.MIGRATION_47_48, AppDatabase.MIGRATION_52_53, AppDatabase.MIGRATION_54_56, + AppDatabase.MIGRATION_47_48, AppDatabase.MIGRATION_52_53, AppDatabase.MIGRATION_54_56 ) .build() } diff --git a/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt index bee62f7ec..2d1a1892b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt @@ -19,10 +19,10 @@ package com.keylesspalace.tusky.di import dagger.Module import dagger.Provides +import javax.inject.Qualifier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import javax.inject.Qualifier /** * Scope for potentially long-running tasks that should outlive the viewmodel that diff --git a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt index 03b4ad394..fd7c9767e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt @@ -35,6 +35,12 @@ import com.keylesspalace.tusky.settings.ProxyConfiguration import com.keylesspalace.tusky.util.getNonNullString import dagger.Module import dagger.Provides +import java.net.IDN +import java.net.InetSocketAddress +import java.net.Proxy +import java.util.Date +import java.util.concurrent.TimeUnit +import javax.inject.Singleton import okhttp3.Cache import okhttp3.OkHttp import okhttp3.OkHttpClient @@ -43,12 +49,6 @@ import retrofit2.Retrofit import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import retrofit2.create -import java.net.IDN -import java.net.InetSocketAddress -import java.net.Proxy -import java.util.Date -import java.util.concurrent.TimeUnit -import javax.inject.Singleton /** * Created by charlag on 3/24/18. @@ -104,7 +104,9 @@ class NetworkModule { .apply { addInterceptor(InstanceSwitchAuthInterceptor(accountManager)) if (BuildConfig.DEBUG) { - addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC }) + addInterceptor( + HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC } + ) } } .build() @@ -112,10 +114,7 @@ class NetworkModule { @Provides @Singleton - fun providesRetrofit( - httpClient: OkHttpClient, - gson: Gson - ): Retrofit { + fun providesRetrofit(httpClient: OkHttpClient, gson: Gson): Retrofit { return Retrofit.Builder().baseUrl("https://" + MastodonApi.PLACEHOLDER_DOMAIN) .client(httpClient) .addConverterFactory(GsonConverterFactory.create(gson)) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt b/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt index dc4d03449..d2c86a598 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/ViewModelFactory.kt @@ -54,12 +54,19 @@ import javax.inject.Singleton import kotlin.reflect.KClass @Singleton -class ViewModelFactory @Inject constructor(private val viewModels: MutableMap, Provider>) : ViewModelProvider.Factory { +class ViewModelFactory @Inject constructor( + private val viewModels: MutableMap, Provider> +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T = viewModels[modelClass]?.get() as T + override fun create(modelClass: Class): T = + viewModels[modelClass]?.get() as T } -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +@Target( + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY_GETTER, + AnnotationTarget.PROPERTY_SETTER +) @Retention(AnnotationRetention.RUNTIME) @MapKey internal annotation class ViewModelKey(val value: KClass) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt index 212d4d319..a2cccf992 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/WorkerModule.kt @@ -36,10 +36,14 @@ abstract class WorkerModule { @Binds @IntoMap @WorkerKey(NotificationWorker::class) - internal abstract fun bindNotificationWorkerFactory(worker: NotificationWorker.Factory): ChildWorkerFactory + internal abstract fun bindNotificationWorkerFactory( + worker: NotificationWorker.Factory + ): ChildWorkerFactory @Binds @IntoMap @WorkerKey(PruneCacheWorker::class) - internal abstract fun bindPruneCacheWorkerFactory(worker: PruneCacheWorker.Factory): ChildWorkerFactory + internal abstract fun bindPruneCacheWorkerFactory( + worker: PruneCacheWorker.Factory + ): ChildWorkerFactory } diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt index 4bb2b52e8..58c879e52 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt @@ -22,7 +22,8 @@ data class Account( val id: String, @SerializedName("username") val localUsername: String, @SerializedName("acct") val username: String, - @SerializedName("display_name") val displayName: String?, // should never be null per Api definition, but some servers break the contract + // should never be null per Api definition, but some servers break the contract + @SerializedName("display_name") val displayName: String?, @SerializedName("created_at") val createdAt: Date, val note: String, val url: String, @@ -34,8 +35,10 @@ data class Account( @SerializedName("statuses_count") val statusesCount: Int = 0, val source: AccountSource? = null, val bot: Boolean = false, - val emojis: List? = emptyList(), // nullable for backward compatibility - val fields: List? = emptyList(), // nullable for backward compatibility + // nullable for backward compatibility + val emojis: List? = emptyList(), + // nullable for backward compatibility + val fields: List? = emptyList(), val moved: Account? = null, val roles: List? = emptyList() ) { diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.kt index 0ebd9c370..c1e938ab0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Attachment.kt @@ -28,7 +28,8 @@ import kotlinx.parcelize.Parcelize data class Attachment( val id: String, val url: String, - @SerializedName("preview_url") val previewUrl: String?, // can be null for e.g. audio attachments + // can be null for e.g. audio attachments + @SerializedName("preview_url") val previewUrl: String?, val meta: MetaData?, val type: Type, val description: String?, @@ -55,7 +56,11 @@ data class Attachment( class MediaTypeDeserializer : JsonDeserializer { @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, classOfT: java.lang.reflect.Type, context: JsonDeserializationContext): Type { + override fun deserialize( + json: JsonElement, + classOfT: java.lang.reflect.Type, + context: JsonDeserializationContext + ): Type { return when (json.toString()) { "\"image\"" -> Type.IMAGE "\"gifv\"" -> Type.GIFV diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Conversation.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Conversation.kt index e5a547f11..554b6cb1d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Conversation.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Conversation.kt @@ -20,6 +20,7 @@ import com.google.gson.annotations.SerializedName data class Conversation( val id: String, val accounts: List, - @SerializedName("last_status") val lastStatus: Status?, // should never be null, but apparently its possible https://github.com/tuskyapp/Tusky/issues/1038 + // should never be null, but apparently its possible https://github.com/tuskyapp/Tusky/issues/1038 + @SerializedName("last_status") val lastStatus: Status?, val unread: Boolean ) diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Filter.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Filter.kt index 236e216e1..d273d58c6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Filter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Filter.kt @@ -2,8 +2,8 @@ package com.keylesspalace.tusky.entity import android.os.Parcelable import com.google.gson.annotations.SerializedName -import kotlinx.parcelize.Parcelize import java.util.Date +import kotlinx.parcelize.Parcelize @Parcelize data class Filter( diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt index 048367535..d3a03194b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Instance.kt @@ -15,7 +15,7 @@ data class Instance( // val registrations: Registrations, // val contact: Contact, val rules: List, - val pleroma: PleromaConfiguration?, + val pleroma: PleromaConfiguration? ) { data class Usage(val users: Users) { data class Users(@SerializedName("active_month") val activeMonth: Int) @@ -23,11 +23,11 @@ data class Instance( data class Thumbnail( val url: String, val blurhash: String?, - val versions: Versions?, + val versions: Versions? ) { data class Versions( @SerializedName("@1x") val at1x: String?, - @SerializedName("@2x") val at2x: String?, + @SerializedName("@2x") val at2x: String? ) } data class Configuration( @@ -36,14 +36,14 @@ data class Instance( val statuses: Statuses?, @SerializedName("media_attachments") val mediaAttachments: MediaAttachments?, val polls: Polls?, - val translation: Translation?, + val translation: Translation? ) { data class Urls(@SerializedName("streaming_api") val streamingApi: String) data class Accounts(@SerializedName("max_featured_tags") val maxFeaturedTags: Int) data class Statuses( @SerializedName("max_characters") val maxCharacters: Int, @SerializedName("max_media_attachments") val maxMediaAttachments: Int, - @SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int, + @SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int ) data class MediaAttachments( // Warning: This is an array in mastodon and a dictionary in friendica @@ -52,20 +52,20 @@ data class Instance( @SerializedName("image_matrix_limit") val imagePixelCountLimit: Long, @SerializedName("video_size_limit") val videoSizeLimitBytes: Long, @SerializedName("video_matrix_limit") val videoPixelCountLimit: Long, - @SerializedName("video_frame_rate_limit") val videoFrameRateLimit: Int, + @SerializedName("video_frame_rate_limit") val videoFrameRateLimit: Int ) data class Polls( @SerializedName("max_options") val maxOptions: Int, @SerializedName("max_characters_per_option") val maxCharactersPerOption: Int, @SerializedName("min_expiration") val minExpirationSeconds: Int, - @SerializedName("max_expiration") val maxExpirationSeconds: Int, + @SerializedName("max_expiration") val maxExpirationSeconds: Int ) data class Translation(val enabled: Boolean) } data class Registrations( val enabled: Boolean, @SerializedName("approval_required") val approvalRequired: Boolean, - val message: String?, + val message: String? ) data class Contact(val email: String, val account: Account) data class Rule(val id: String, val text: String) diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/MastoList.kt b/app/src/main/java/com/keylesspalace/tusky/entity/MastoList.kt index e510f4d2a..119f55299 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/MastoList.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/MastoList.kt @@ -26,7 +26,7 @@ data class MastoList( val id: String, val title: String, val exclusive: Boolean?, - @SerializedName("replies_policy") val repliesPolicy: String?, + @SerializedName("replies_policy") val repliesPolicy: String? ) { enum class ReplyPolicy(val policy: String) { NONE("none"), @@ -34,7 +34,8 @@ data class MastoList( FOLLOWED("followed"); companion object { - fun from(policy: String?): ReplyPolicy = values().firstOrNull { it.policy == policy } ?: LIST + fun from(policy: String?): ReplyPolicy = + values().firstOrNull { it.policy == policy } ?: LIST } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt index 05f992e16..471f036d2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt @@ -78,7 +78,8 @@ data class Notification( } /** Notification types for UI display (omits UNKNOWN) */ - val visibleTypes = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL, STATUS, SIGN_UP, UPDATE, REPORT) + val visibleTypes = + listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL, STATUS, SIGN_UP, UPDATE, REPORT) } override fun toString(): String { @@ -117,8 +118,8 @@ data class Notification( fun rewriteToStatusTypeIfNeeded(accountId: String): Notification { if (type == Type.MENTION && status != null) { return if (status.mentions.any { - it.id == accountId - } + it.id == accountId + } ) { this } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt index 584b76f8c..86a3d8b02 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt @@ -9,7 +9,8 @@ data class Poll( val expired: Boolean, val multiple: Boolean, @SerializedName("votes_count") val votesCount: Int, - @SerializedName("voters_count") val votersCount: Int?, // nullable for compatibility with Pleroma + // nullable for compatibility with Pleroma + @SerializedName("voters_count") val votersCount: Int?, val options: List, val voted: Boolean, @SerializedName("own_votes") val ownVotes: List? diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.kt index e99bcce6d..998545840 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Relationship.kt @@ -34,6 +34,8 @@ data class Relationship( */ @JsonAdapter(GuardedBooleanAdapter::class) val subscribing: Boolean? = null, @SerializedName("domain_blocking") val blockingDomain: Boolean, - val note: String?, // nullable for backward compatibility / feature detection - val notifying: Boolean? // since 3.3.0rc + // nullable for backward compatibility / feature detection + val note: String?, + // since 3.3.0rc + val notifying: Boolean? ) diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt index 0b9a0796d..18d5faec5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt @@ -23,7 +23,8 @@ import java.util.Date data class Status( val id: String, - val url: String?, // not present if it's reblog + // not present if it's reblog + val url: String?, val account: TimelineAccount, @SerializedName("in_reply_to_id") val inReplyToId: String?, @SerializedName("in_reply_to_account_id") val inReplyToAccountId: String?, diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/TimelineAccount.kt b/app/src/main/java/com/keylesspalace/tusky/entity/TimelineAccount.kt index f801d4969..a7d9f8822 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/TimelineAccount.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/TimelineAccount.kt @@ -25,12 +25,14 @@ data class TimelineAccount( val id: String, @SerializedName("username") val localUsername: String, @SerializedName("acct") val username: String, - @SerializedName("display_name") val displayName: String?, // should never be null per Api definition, but some servers break the contract + // should never be null per Api definition, but some servers break the contract + @SerializedName("display_name") val displayName: String?, val url: String, val avatar: String, val note: String, val bot: Boolean = false, - val emojis: List? = emptyList() // nullable for backward compatibility + // nullable for backward compatibility + val emojis: List? = emptyList() ) { val name: String diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt index 3cb32134b..73617ae41 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.kt @@ -59,8 +59,8 @@ import com.keylesspalace.tusky.util.openLink import com.keylesspalace.tusky.util.parseAsMastodonHtml import com.keylesspalace.tusky.view.showMuteAccountDialog import com.keylesspalace.tusky.viewdata.AttachmentViewData -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch /* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an * awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature @@ -158,7 +158,14 @@ abstract class SFragment : Fragment(), Injectable { val menu = popup.menu when (status.visibility) { Status.Visibility.PUBLIC, Status.Visibility.UNLISTED -> { - menu.add(0, R.id.pin, 1, getString(if (status.isPinned()) R.string.unpin_action else R.string.pin_action)) + menu.add( + 0, + R.id.pin, + 1, + getString( + if (status.isPinned()) R.string.unpin_action else R.string.pin_action + ) + ) } Status.Visibility.PRIVATE -> { val reblogged = status.reblog?.reblogged ?: status.reblogged @@ -227,7 +234,11 @@ abstract class SFragment : Fragment(), Injectable { return@setOnMenuItemClickListener true } R.id.status_copy_link -> { - (requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply { + ( + requireActivity().getSystemService( + Context.CLIPBOARD_SERVICE + ) as ClipboardManager + ).apply { setPrimaryClip(ClipData.newPlainText(null, statusUrl)) } return@setOnMenuItemClickListener true @@ -295,7 +306,10 @@ abstract class SFragment : Fragment(), Injectable { } private fun onMute(accountId: String, accountUsername: String) { - showMuteAccountDialog(this.requireActivity(), accountUsername) { notifications: Boolean?, duration: Int? -> + showMuteAccountDialog( + this.requireActivity(), + accountUsername + ) { notifications: Boolean?, duration: Int? -> lifecycleScope.launch { timelineCases.mute(accountId, notifications == true, duration) } @@ -459,13 +473,18 @@ abstract class SFragment : Fragment(), Injectable { private fun downloadAllMedia(status: Status) { Toast.makeText(context, R.string.downloading_media, Toast.LENGTH_SHORT).show() - val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + val downloadManager = requireActivity().getSystemService( + Context.DOWNLOAD_SERVICE + ) as DownloadManager for ((_, url) in status.attachments) { val uri = Uri.parse(url) downloadManager.enqueue( DownloadManager.Request(uri).apply { - setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uri.lastPathSegment) + setDestinationInExternalPublicDir( + Environment.DIRECTORY_DOWNLOADS, + uri.lastPathSegment + ) } ) } @@ -492,7 +511,10 @@ abstract class SFragment : Fragment(), Injectable { companion object { private const val TAG = "SFragment" - private fun accountIsInMentions(account: AccountEntity?, mentions: List): Boolean { + private fun accountIsInMentions( + account: AccountEntity?, + mentions: List + ): Boolean { return mentions.any { mention -> account?.username == mention.username && account.domain == Uri.parse(mention.url)?.host } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt index b6bedbc48..21245f688 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt @@ -85,7 +85,11 @@ class ViewImageFragment : ViewMediaFragment() { loadImageFromNetwork(url, previewUrl, binding.photoView) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { toolbar = (requireActivity() as ViewMediaActivity).toolbar this.transition = BehaviorSubject.create() return inflater.inflate(R.layout.fragment_view_image, container, false) @@ -96,7 +100,11 @@ class ViewImageFragment : ViewMediaFragment() { super.onViewCreated(view, savedInstanceState) val arguments = this.requireArguments() - val attachment = BundleCompat.getParcelable(arguments, ARG_ATTACHMENT, Attachment::class.java) + val attachment = BundleCompat.getParcelable( + arguments, + ARG_ATTACHMENT, + Attachment::class.java + ) this.shouldStartTransition = arguments.getBoolean(ARG_START_POSTPONED_TRANSITION) val url: String? var description: String? = null diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt index 3125e556e..2f37b422d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt @@ -50,7 +50,10 @@ abstract class ViewMediaFragment : Fragment() { @JvmStatic @OptIn(UnstableApi::class) - fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment { + fun newInstance( + attachment: Attachment, + shouldStartPostponedTransition: Boolean + ): ViewMediaFragment { val arguments = Bundle(2) arguments.putParcelable(ARG_ATTACHMENT, attachment) arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, shouldStartPostponedTransition) @@ -85,7 +88,12 @@ abstract class ViewMediaFragment : Fragment() { showingDescription = !TextUtils.isEmpty(description) isDescriptionVisible = showingDescription - setupMediaView(url, previewUrl, description, showingDescription && mediaActivity.isToolbarVisible) + setupMediaView( + url, + previewUrl, + description, + showingDescription && mediaActivity.isToolbarVisible + ) toolbarVisibilityDisposable = (activity as ViewMediaActivity) .addToolbarVisibilityListener { isVisible -> diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt index 80fe55add..0967bb766 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt @@ -58,9 +58,9 @@ import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible -import okhttp3.OkHttpClient import javax.inject.Inject import kotlin.math.abs +import okhttp3.OkHttpClient @UnstableApi class ViewVideoFragment : ViewMediaFragment(), Injectable { @@ -113,13 +113,19 @@ class ViewVideoFragment : ViewMediaFragment(), Injectable { } @SuppressLint("PrivateResource", "MissingInflatedId") - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { mediaActivity = activity as ViewMediaActivity toolbar = mediaActivity.toolbar val rootView = inflater.inflate(R.layout.fragment_view_video, container, false) // Move the controls to the bottom of the screen, with enough bottom margin to clear the seekbar - val controls = rootView.findViewById(androidx.media3.ui.R.id.exo_center_controls) + val controls = rootView.findViewById( + androidx.media3.ui.R.id.exo_center_controls + ) val layoutParams = controls.layoutParams as FrameLayout.LayoutParams layoutParams.gravity = Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM layoutParams.bottomMargin = rootView.context.resources.getDimension(androidx.media3.ui.R.dimen.exo_styled_bottom_bar_height) @@ -147,7 +153,9 @@ class ViewVideoFragment : ViewMediaFragment(), Injectable { /** The view that contains the playing content */ // binding.videoView is fullscreen, and includes the controls, so don't use that // when scaling in response to the user dragging on the screen - val contentFrame = binding.videoView.findViewById(androidx.media3.ui.R.id.exo_content_frame) + val contentFrame = binding.videoView.findViewById( + androidx.media3.ui.R.id.exo_content_frame + ) /** Handle taps and flings */ val simpleGestureDetector = GestureDetectorCompat( diff --git a/app/src/main/java/com/keylesspalace/tusky/json/GuardedBooleanAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/json/GuardedBooleanAdapter.kt index 8ed702a81..0c8a298bd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/json/GuardedBooleanAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/json/GuardedBooleanAdapter.kt @@ -23,7 +23,11 @@ import java.lang.reflect.Type class GuardedBooleanAdapter : JsonDeserializer { @Throws(JsonParseException::class) - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Boolean? { + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext + ): Boolean? { return if (json.isJsonObject) { null } else { diff --git a/app/src/main/java/com/keylesspalace/tusky/json/Iso8601Utils.kt b/app/src/main/java/com/keylesspalace/tusky/json/Iso8601Utils.kt index 983333a9c..0a4c9a059 100644 --- a/app/src/main/java/com/keylesspalace/tusky/json/Iso8601Utils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/json/Iso8601Utils.kt @@ -82,19 +82,40 @@ internal fun String.parseIsoDate(): Date { var offset = 0 // extract year - val year = parseInt(this, offset, 4.let { offset += it; offset }) + val year = parseInt( + this, + offset, + 4.let { + offset += it + offset + } + ) if (checkOffset(this, offset, '-')) { offset += 1 } // extract month - val month = parseInt(this, offset, 2.let { offset += it; offset }) + val month = parseInt( + this, + offset, + 2.let { + offset += it + offset + } + ) if (checkOffset(this, offset, '-')) { offset += 1 } // extract day - val day = parseInt(this, offset, 2.let { offset += it; offset }) + val day = parseInt( + this, + offset, + 2.let { + offset += it + offset + } + ) // default time value var hour = 0 var minutes = 0 @@ -109,11 +130,27 @@ internal fun String.parseIsoDate(): Date { } if (hasT) { // extract hours, minutes, seconds and milliseconds - hour = parseInt(this, 1.let { offset += it; offset }, 2.let { offset += it; offset }) + hour = parseInt( + this, + 1.let { + offset += it + offset + }, + 2.let { + offset += it + offset + } + ) if (checkOffset(this, offset, ':')) { offset += 1 } - minutes = parseInt(this, offset, 2.let { offset += it; offset }) + minutes = parseInt( + this, offset, + 2.let { + offset += it + offset + } + ) if (checkOffset(this, offset, ':')) { offset += 1 } @@ -121,12 +158,21 @@ internal fun String.parseIsoDate(): Date { if (this.length > offset) { val c = this[offset] if (c != 'Z' && c != '+' && c != '-') { - seconds = parseInt(this, offset, 2.let { offset += it; offset }) + seconds = parseInt( + this, offset, + 2.let { + offset += it + offset + } + ) if (seconds in 60..62) seconds = 59 // truncate up to 3 leap seconds // milliseconds can be optional in the format if (checkOffset(this, offset, '.')) { offset += 1 - val endOffset = indexOfNonDigit(this, offset + 1) // assume at least one digit + val endOffset = indexOfNonDigit( + this, + offset + 1 + ) // assume at least one digit val parseEndOffset = min(endOffset, offset + 3) // parse up to 3 digits val fraction = parseInt(this, offset, parseEndOffset) milliseconds = diff --git a/app/src/main/java/com/keylesspalace/tusky/network/InstanceSwitchAuthInterceptor.kt b/app/src/main/java/com/keylesspalace/tusky/network/InstanceSwitchAuthInterceptor.kt index 6033165f9..6aabaa13d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/InstanceSwitchAuthInterceptor.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/InstanceSwitchAuthInterceptor.kt @@ -17,6 +17,7 @@ package com.keylesspalace.tusky.network import android.util.Log import com.keylesspalace.tusky.db.AccountManager +import java.io.IOException import okhttp3.HttpUrl import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType @@ -24,7 +25,6 @@ import okhttp3.Protocol import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody -import java.io.IOException class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager) : Interceptor { @@ -57,7 +57,10 @@ class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager) val newRequest: Request = builder.build() if (MastodonApi.PLACEHOLDER_DOMAIN == newRequest.url.host) { - Log.w("ISAInterceptor", "no user logged in or no domain header specified - can't make request to " + newRequest.url) + Log.w( + "ISAInterceptor", + "no user logged in or no domain header specified - can't make request to " + newRequest.url + ) return Response.Builder() .code(400) .message("Bad Request") diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 1554aa6ac..4871a454b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -85,7 +85,9 @@ interface MastodonApi { suspend fun getCustomEmojis(): NetworkResult> @GET("api/v1/instance") - suspend fun getInstanceV1(@Header(DOMAIN_HEADER) domain: String? = null): NetworkResult + suspend fun getInstanceV1( + @Header(DOMAIN_HEADER) domain: String? = null + ): NetworkResult @GET("api/v2/instance") suspend fun getInstance(): NetworkResult @@ -94,9 +96,7 @@ interface MastodonApi { suspend fun getFiltersV1(): NetworkResult> @GET("api/v2/filters/{filterId}") - suspend fun getFilter( - @Path("filterId") filterId: String - ): NetworkResult + suspend fun getFilter(@Path("filterId") filterId: String): NetworkResult @GET("api/v2/filters") suspend fun getFilters(): NetworkResult> @@ -158,9 +158,7 @@ interface MastodonApi { /** Fetch a single notification */ @GET("api/v1/notifications/{id}") - suspend fun notification( - @Path("id") id: String - ): Response + suspend fun notification(@Path("id") id: String): Response @GET("api/v1/markers") suspend fun markersWithAuth( @@ -201,9 +199,7 @@ interface MastodonApi { ): NetworkResult @GET("api/v1/media/{mediaId}") - suspend fun getMedia( - @Path("mediaId") mediaId: String - ): Response + suspend fun getMedia(@Path("mediaId") mediaId: String): Response @POST("api/v1/statuses") suspend fun createStatus( @@ -214,9 +210,7 @@ interface MastodonApi { ): NetworkResult @GET("api/v1/statuses/{id}") - suspend fun status( - @Path("id") statusId: String - ): NetworkResult + suspend fun status(@Path("id") statusId: String): NetworkResult @PUT("api/v1/statuses/{id}") suspend fun editStatus( @@ -228,24 +222,16 @@ interface MastodonApi { ): NetworkResult @GET("api/v1/statuses/{id}") - suspend fun statusAsync( - @Path("id") statusId: String - ): NetworkResult + suspend fun statusAsync(@Path("id") statusId: String): NetworkResult @GET("api/v1/statuses/{id}/source") - suspend fun statusSource( - @Path("id") statusId: String - ): NetworkResult + suspend fun statusSource(@Path("id") statusId: String): NetworkResult @GET("api/v1/statuses/{id}/context") - suspend fun statusContext( - @Path("id") statusId: String - ): NetworkResult + suspend fun statusContext(@Path("id") statusId: String): NetworkResult @GET("api/v1/statuses/{id}/history") - suspend fun statusEdits( - @Path("id") statusId: String - ): NetworkResult> + suspend fun statusEdits(@Path("id") statusId: String): NetworkResult> @GET("api/v1/statuses/{id}/reblogged_by") suspend fun statusRebloggedBy( @@ -260,99 +246,61 @@ interface MastodonApi { ): Response> @DELETE("api/v1/statuses/{id}") - suspend fun deleteStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun deleteStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/reblog") - suspend fun reblogStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun reblogStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/unreblog") - suspend fun unreblogStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun unreblogStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/favourite") - suspend fun favouriteStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun favouriteStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/unfavourite") - suspend fun unfavouriteStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun unfavouriteStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/bookmark") - suspend fun bookmarkStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun bookmarkStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/unbookmark") - suspend fun unbookmarkStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun unbookmarkStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/reblog") - fun reblogStatusOld( - @Path("id") statusId: String - ): Single + fun reblogStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/unreblog") - fun unreblogStatusOld( - @Path("id") statusId: String - ): Single + fun unreblogStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/favourite") - fun favouriteStatusOld( - @Path("id") statusId: String - ): Single + fun favouriteStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/unfavourite") - fun unfavouriteStatusOld( - @Path("id") statusId: String - ): Single + fun unfavouriteStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/bookmark") - fun bookmarkStatusOld( - @Path("id") statusId: String - ): Single + fun bookmarkStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/unbookmark") - fun unbookmarkStatusOld( - @Path("id") statusId: String - ): Single + fun unbookmarkStatusOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/pin") - suspend fun pinStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun pinStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/unpin") - suspend fun unpinStatus( - @Path("id") statusId: String - ): NetworkResult + suspend fun unpinStatus(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/mute") - suspend fun muteConversation( - @Path("id") statusId: String - ): NetworkResult + suspend fun muteConversation(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/unmute") - suspend fun unmuteConversation( - @Path("id") statusId: String - ): NetworkResult + suspend fun unmuteConversation(@Path("id") statusId: String): NetworkResult @POST("api/v1/statuses/{id}/mute") - fun muteConversationOld( - @Path("id") statusId: String - ): Single + fun muteConversationOld(@Path("id") statusId: String): Single @POST("api/v1/statuses/{id}/unmute") - fun unmuteConversationOld( - @Path("id") statusId: String - ): Single + fun unmuteConversationOld(@Path("id") statusId: String): Single @GET("api/v1/scheduled_statuses") fun scheduledStatuses( @@ -414,9 +362,7 @@ interface MastodonApi { ): NetworkResult> @GET("api/v1/accounts/{id}") - suspend fun account( - @Path("id") accountId: String - ): NetworkResult + suspend fun account(@Path("id") accountId: String): NetworkResult /** * Method to fetch statuses for the specified account. @@ -459,19 +405,13 @@ interface MastodonApi { ): NetworkResult @POST("api/v1/accounts/{id}/unfollow") - suspend fun unfollowAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun unfollowAccount(@Path("id") accountId: String): NetworkResult @POST("api/v1/accounts/{id}/block") - suspend fun blockAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun blockAccount(@Path("id") accountId: String): NetworkResult @POST("api/v1/accounts/{id}/unblock") - suspend fun unblockAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun unblockAccount(@Path("id") accountId: String): NetworkResult @FormUrlEncoded @POST("api/v1/accounts/{id}/mute") @@ -482,9 +422,7 @@ interface MastodonApi { ): NetworkResult @POST("api/v1/accounts/{id}/unmute") - suspend fun unmuteAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun unmuteAccount(@Path("id") accountId: String): NetworkResult @GET("api/v1/accounts/relationships") suspend fun relationships( @@ -492,24 +430,16 @@ interface MastodonApi { ): NetworkResult> @POST("api/v1/pleroma/accounts/{id}/subscribe") - suspend fun subscribeAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun subscribeAccount(@Path("id") accountId: String): NetworkResult @POST("api/v1/pleroma/accounts/{id}/unsubscribe") - suspend fun unsubscribeAccount( - @Path("id") accountId: String - ): NetworkResult + suspend fun unsubscribeAccount(@Path("id") accountId: String): NetworkResult @GET("api/v1/blocks") - suspend fun blocks( - @Query("max_id") maxId: String? - ): Response> + suspend fun blocks(@Query("max_id") maxId: String?): Response> @GET("api/v1/mutes") - suspend fun mutes( - @Query("max_id") maxId: String? - ): Response> + suspend fun mutes(@Query("max_id") maxId: String?): Response> @GET("api/v1/domain_blocks") suspend fun domainBlocks( @@ -520,9 +450,7 @@ interface MastodonApi { @FormUrlEncoded @POST("api/v1/domain_blocks") - suspend fun blockDomain( - @Field("domain") domain: String - ): NetworkResult + suspend fun blockDomain(@Field("domain") domain: String): NetworkResult @FormUrlEncoded // @DELETE doesn't support fields @@ -544,19 +472,13 @@ interface MastodonApi { ): Response> @GET("api/v1/follow_requests") - suspend fun followRequests( - @Query("max_id") maxId: String? - ): Response> + suspend fun followRequests(@Query("max_id") maxId: String?): Response> @POST("api/v1/follow_requests/{id}/authorize") - fun authorizeFollowRequest( - @Path("id") accountId: String - ): Single + fun authorizeFollowRequest(@Path("id") accountId: String): Single @POST("api/v1/follow_requests/{id}/reject") - fun rejectFollowRequest( - @Path("id") accountId: String - ): Single + fun rejectFollowRequest(@Path("id") accountId: String): Single @FormUrlEncoded @POST("api/v1/apps") @@ -600,7 +522,7 @@ interface MastodonApi { suspend fun createList( @Field("title") title: String, @Field("exclusive") exclusive: Boolean?, - @Field("replies_policy") replyPolicy: String, + @Field("replies_policy") replyPolicy: String ): NetworkResult @FormUrlEncoded @@ -609,13 +531,11 @@ interface MastodonApi { @Path("listId") listId: String, @Field("title") title: String, @Field("exclusive") exclusive: Boolean?, - @Field("replies_policy") replyPolicy: String, + @Field("replies_policy") replyPolicy: String ): NetworkResult @DELETE("api/v1/lists/{listId}") - suspend fun deleteList( - @Path("listId") listId: String - ): NetworkResult + suspend fun deleteList(@Path("listId") listId: String): NetworkResult @GET("api/v1/lists/{listId}/accounts") suspend fun getAccountsInList( @@ -645,9 +565,7 @@ interface MastodonApi { ): Response> @DELETE("/api/v1/conversations/{id}") - suspend fun deleteConversation( - @Path("id") conversationId: String - ) + suspend fun deleteConversation(@Path("id") conversationId: String) @FormUrlEncoded @POST("api/v1/filters") @@ -671,9 +589,7 @@ interface MastodonApi { ): NetworkResult @DELETE("api/v1/filters/{id}") - suspend fun deleteFilterV1( - @Path("id") id: String - ): NetworkResult + suspend fun deleteFilterV1(@Path("id") id: String): NetworkResult @FormUrlEncoded @POST("api/v2/filters") @@ -695,9 +611,7 @@ interface MastodonApi { ): NetworkResult @DELETE("api/v2/filters/{id}") - suspend fun deleteFilter( - @Path("id") id: String - ): NetworkResult + suspend fun deleteFilter(@Path("id") id: String): NetworkResult @FormUrlEncoded @POST("api/v2/filters/{filterId}/keywords") @@ -729,10 +643,7 @@ interface MastodonApi { @FormUrlEncoded @POST("api/v1/polls/{id}/votes") - fun voteInPollOld( - @Path("id") id: String, - @Field("choices[]") choices: List - ): Single + fun voteInPollOld(@Path("id") id: String, @Field("choices[]") choices: List): Single @GET("api/v1/announcements") suspend fun listAnnouncements( @@ -740,9 +651,7 @@ interface MastodonApi { ): NetworkResult> @POST("api/v1/announcements/{id}/dismiss") - suspend fun dismissAnnouncement( - @Path("id") announcementId: String - ): NetworkResult + suspend fun dismissAnnouncement(@Path("id") announcementId: String): NetworkResult @PUT("api/v1/announcements/{id}/reactions/{name}") suspend fun addAnnouncementReaction( @@ -776,9 +685,7 @@ interface MastodonApi { ): Single> @GET("api/v1/statuses/{id}") - fun statusObservable( - @Path("id") statusId: String - ): Single + fun statusObservable(@Path("id") statusId: String): Single @GET("api/v2/search") fun searchObservable( diff --git a/app/src/main/java/com/keylesspalace/tusky/network/ProgressRequestBody.kt b/app/src/main/java/com/keylesspalace/tusky/network/ProgressRequestBody.kt index f74b0c1a2..3a420139c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/ProgressRequestBody.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/ProgressRequestBody.kt @@ -14,11 +14,11 @@ * see . */ package com.keylesspalace.tusky.network +import java.io.IOException +import java.io.InputStream import okhttp3.MediaType import okhttp3.RequestBody import okio.BufferedSink -import java.io.IOException -import java.io.InputStream class ProgressRequestBody(private val content: InputStream, private val contentLength: Long, private val mediaType: MediaType, private val uploadListener: UploadCallback) : RequestBody() { fun interface UploadCallback { diff --git a/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.kt index 26c5fc05a..80a30adea 100644 --- a/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/pager/ImagePagerAdapter.kt @@ -14,7 +14,8 @@ class ImagePagerAdapter( ) : ViewMediaAdapter(activity) { private var didTransition = false - private val fragments = MutableList?>(attachments.size) { null } + private val fragments = + MutableList?>(attachments.size) { null } override fun getItemCount() = attachments.size diff --git a/app/src/main/java/com/keylesspalace/tusky/pager/MainPagerAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/pager/MainPagerAdapter.kt index c635f929c..a8d9c7069 100644 --- a/app/src/main/java/com/keylesspalace/tusky/pager/MainPagerAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/pager/MainPagerAdapter.kt @@ -20,7 +20,9 @@ import androidx.fragment.app.FragmentActivity import com.keylesspalace.tusky.TabData import com.keylesspalace.tusky.util.CustomFragmentStateAdapter -class MainPagerAdapter(var tabs: List, activity: FragmentActivity) : CustomFragmentStateAdapter(activity) { +class MainPagerAdapter(var tabs: List, activity: FragmentActivity) : CustomFragmentStateAdapter( + activity +) { override fun createFragment(position: Int): Fragment { val tab = tabs[position] diff --git a/app/src/main/java/com/keylesspalace/tusky/receiver/NotificationBlockStateBroadcastReceiver.kt b/app/src/main/java/com/keylesspalace/tusky/receiver/NotificationBlockStateBroadcastReceiver.kt index 20b18a9f9..c1636e921 100644 --- a/app/src/main/java/com/keylesspalace/tusky/receiver/NotificationBlockStateBroadcastReceiver.kt +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/NotificationBlockStateBroadcastReceiver.kt @@ -26,10 +26,10 @@ import com.keylesspalace.tusky.components.notifications.updateUnifiedPushSubscri import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.network.MastodonApi import dagger.android.AndroidInjection +import javax.inject.Inject import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import javax.inject.Inject @DelicateCoroutinesApi class NotificationBlockStateBroadcastReceiver : BroadcastReceiver() { @@ -60,7 +60,14 @@ class NotificationBlockStateBroadcastReceiver : BroadcastReceiver() { accountManager.getAccountByIdentifier(gid)?.let { account -> if (isUnifiedPushNotificationEnabledForAccount(account)) { // Update UnifiedPush notification subscription - GlobalScope.launch { updateUnifiedPushSubscription(context, mastodonApi, accountManager, account) } + GlobalScope.launch { + updateUnifiedPushSubscription( + context, + mastodonApi, + accountManager, + account + ) + } } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt index 861edae60..d175522b3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt @@ -47,10 +47,16 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() { if (intent.action == NotificationHelper.REPLY_ACTION) { val notificationId = intent.getIntExtra(NotificationHelper.KEY_NOTIFICATION_ID, -1) val senderId = intent.getLongExtra(NotificationHelper.KEY_SENDER_ACCOUNT_ID, -1) - val senderIdentifier = intent.getStringExtra(NotificationHelper.KEY_SENDER_ACCOUNT_IDENTIFIER) - val senderFullName = intent.getStringExtra(NotificationHelper.KEY_SENDER_ACCOUNT_FULL_NAME) + val senderIdentifier = intent.getStringExtra( + NotificationHelper.KEY_SENDER_ACCOUNT_IDENTIFIER + ) + val senderFullName = intent.getStringExtra( + NotificationHelper.KEY_SENDER_ACCOUNT_FULL_NAME + ) val citedStatusId = intent.getStringExtra(NotificationHelper.KEY_CITED_STATUS_ID) - val visibility = intent.getSerializableExtra(NotificationHelper.KEY_VISIBILITY) as Status.Visibility + val visibility = intent.getSerializableExtra( + NotificationHelper.KEY_VISIBILITY + ) as Status.Visibility val spoiler = intent.getStringExtra(NotificationHelper.KEY_SPOILER).orEmpty() val mentions = intent.getStringArrayExtra(NotificationHelper.KEY_MENTIONS).orEmpty() @@ -63,7 +69,10 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() { if (account == null) { Log.w(TAG, "Account \"$senderId\" not found in database. Aborting quick reply!") - val builder = NotificationCompat.Builder(context, NotificationHelper.CHANNEL_MENTION + senderIdentifier) + val builder = NotificationCompat.Builder( + context, + NotificationHelper.CHANNEL_MENTION + senderIdentifier + ) .setSmallIcon(R.drawable.ic_notify) .setColor(context.getColor(R.color.tusky_blue)) .setGroup(senderFullName) @@ -105,7 +114,10 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() { context.startService(sendIntent) - val builder = NotificationCompat.Builder(context, NotificationHelper.CHANNEL_MENTION + senderIdentifier) + val builder = NotificationCompat.Builder( + context, + NotificationHelper.CHANNEL_MENTION + senderIdentifier + ) .setSmallIcon(R.drawable.ic_notify) .setColor(context.getColor(R.color.notification_color)) .setGroup(senderFullName) diff --git a/app/src/main/java/com/keylesspalace/tusky/receiver/UnifiedPushBroadcastReceiver.kt b/app/src/main/java/com/keylesspalace/tusky/receiver/UnifiedPushBroadcastReceiver.kt index b95e53105..174aca5aa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/receiver/UnifiedPushBroadcastReceiver.kt +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/UnifiedPushBroadcastReceiver.kt @@ -26,11 +26,11 @@ import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.worker.NotificationWorker import dagger.android.AndroidInjection +import javax.inject.Inject import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.unifiedpush.android.connector.MessagingReceiver -import javax.inject.Inject @DelicateCoroutinesApi class UnifiedPushBroadcastReceiver : MessagingReceiver() { @@ -63,7 +63,9 @@ class UnifiedPushBroadcastReceiver : MessagingReceiver() { accountManager.getAccountById(instance.toLong())?.let { // Launch the coroutine in global scope -- it is short and we don't want to lose the registration event // and there is no saner way to use structured concurrency in a receiver - GlobalScope.launch { registerUnifiedPushEndpoint(context, mastodonApi, accountManager, it, endpoint) } + GlobalScope.launch { + registerUnifiedPushEndpoint(context, mastodonApi, accountManager, it, endpoint) + } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt b/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt index 5279103a5..63eac6347 100644 --- a/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt +++ b/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt @@ -53,6 +53,9 @@ import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.unsafeLazy import dagger.android.AndroidInjection +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.TimeUnit +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -61,9 +64,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import retrofit2.HttpException -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.TimeUnit -import javax.inject.Inject class SendStatusService : Service(), Injectable { @@ -88,7 +88,9 @@ class SendStatusService : Service(), Injectable { private val statusesToSend = ConcurrentHashMap() private val sendJobs = ConcurrentHashMap() - private val notificationManager by unsafeLazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } + private val notificationManager by unsafeLazy { + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + } override fun onCreate() { AndroidInjection.inject(this) @@ -103,7 +105,12 @@ class SendStatusService : Service(), Injectable { ?: throw IllegalStateException("SendStatusService started without $KEY_STATUS extra") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel(CHANNEL_ID, getString(R.string.send_post_notification_channel_name), NotificationManager.IMPORTANCE_LOW) + val channel = + NotificationChannel( + CHANNEL_ID, + getString(R.string.send_post_notification_channel_name), + NotificationManager.IMPORTANCE_LOW + ) notificationManager.createNotificationChannel(channel) } @@ -119,7 +126,11 @@ class SendStatusService : Service(), Injectable { .setProgress(1, 0, true) .setOngoing(true) .setColor(getColor(R.color.notification_color)) - .addAction(0, getString(android.R.string.cancel), cancelSendingIntent(sendingNotificationId)) + .addAction( + 0, + getString(android.R.string.cancel), + cancelSendingIntent(sendingNotificationId) + ) if (statusesToSend.size == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH) @@ -296,7 +307,9 @@ class SendStatusService : Service(), Injectable { // when statusToSend == null, sending has been canceled val statusToSend = statusesToSend[statusId] ?: return - val backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong()).coerceAtMost(MAX_RETRY_INTERVAL) + val backoff = TimeUnit.SECONDS.toMillis( + statusToSend.retries.toLong() + ).coerceAtMost(MAX_RETRY_INTERVAL) delay(backoff) sendStatus(statusId) @@ -304,7 +317,10 @@ class SendStatusService : Service(), Injectable { private fun stopSelfWhenDone() { if (statusesToSend.isEmpty()) { - ServiceCompat.stopForeground(this@SendStatusService, ServiceCompat.STOP_FOREGROUND_REMOVE) + ServiceCompat.stopForeground( + this@SendStatusService, + ServiceCompat.STOP_FOREGROUND_REMOVE + ) stopSelf() } } @@ -431,10 +447,7 @@ class SendStatusService : Service(), Injectable { private var sendingNotificationId = -1 // use negative ids to not clash with other notis private var errorNotificationId = Int.MIN_VALUE // use even more negative ids to not clash with other notis - fun sendStatusIntent( - context: Context, - statusToSend: StatusToSend - ): Intent { + fun sendStatusIntent(context: Context, statusToSend: StatusToSend): Intent { val intent = Intent(context, SendStatusService::class.java) intent.putExtra(KEY_STATUS, statusToSend) @@ -482,7 +495,8 @@ data class StatusToSend( @Parcelize data class MediaToSend( val localId: Int, - val id: String?, // null if media is not yet completely uploaded + // null if media is not yet completely uploaded + val id: String?, val uri: String, val description: String?, val focus: Attachment.Focus?, diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt index b50deebd5..479b43310 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt @@ -6,9 +6,9 @@ import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.di.ApplicationScope +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import javax.inject.Inject class AccountPreferenceDataStore @Inject constructor( private val accountManager: AccountManager, diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/ProxyConfiguration.kt b/app/src/main/java/com/keylesspalace/tusky/settings/ProxyConfiguration.kt index fbe8084bc..db8046c78 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/ProxyConfiguration.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/ProxyConfiguration.kt @@ -31,6 +31,13 @@ class ProxyConfiguration private constructor( } } -private val PROXY_RANGE = IntRange(ProxyConfiguration.MIN_PROXY_PORT, ProxyConfiguration.MAX_PROXY_PORT) -private val IP_ADDRESS_REGEX = Regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") -private val HOSTNAME_REGEX = Regex("^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$") +private val PROXY_RANGE = + IntRange(ProxyConfiguration.MIN_PROXY_PORT, ProxyConfiguration.MAX_PROXY_PORT) +private val IP_ADDRESS_REGEX = + Regex( + "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" + ) +private val HOSTNAME_REGEX = + Regex( + "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$" + ) diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt index cb8b13e67..c1b57427d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/SettingsDSL.kt @@ -35,7 +35,10 @@ inline fun PreferenceParent.listPreference(builder: ListPreference.() -> Unit): return pref } -inline fun PreferenceParent.emojiPreference(activity: A, builder: EmojiPickerPreference.() -> Unit): EmojiPickerPreference +inline fun PreferenceParent.emojiPreference( + activity: A, + builder: EmojiPickerPreference.() -> Unit +): EmojiPickerPreference where A : Context, A : ActivityResultRegistryOwner, A : LifecycleOwner { val pref = EmojiPickerPreference.get(activity) builder(pref) diff --git a/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt b/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt index 5239578a0..d34f0e383 100644 --- a/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt +++ b/app/src/main/java/com/keylesspalace/tusky/usecase/TimelineCases.kt @@ -173,7 +173,11 @@ class TimelineCases @Inject constructor( }) } - suspend fun voteInPoll(statusId: String, pollId: String, choices: List): NetworkResult { + suspend fun voteInPoll( + statusId: String, + pollId: String, + choices: List + ): NetworkResult { if (choices.isEmpty()) { return NetworkResult.failure(IllegalStateException()) } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatter.kt b/app/src/main/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatter.kt index 86f2e66fa..dd40b8438 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatter.kt @@ -22,10 +22,22 @@ import java.util.Locale import java.util.TimeZone class AbsoluteTimeFormatter @JvmOverloads constructor(private val tz: TimeZone = TimeZone.getDefault()) { - private val sameDaySdf = SimpleDateFormat("HH:mm", Locale.getDefault()).apply { this.timeZone = tz } - private val sameYearSdf = SimpleDateFormat("dd MMM, HH:mm", Locale.getDefault()).apply { this.timeZone = tz } - private val otherYearSdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).apply { this.timeZone = tz } - private val otherYearCompleteSdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).apply { this.timeZone = tz } + private val sameDaySdf = SimpleDateFormat( + "HH:mm", + Locale.getDefault() + ).apply { this.timeZone = tz } + private val sameYearSdf = SimpleDateFormat("dd MMM, HH:mm", Locale.getDefault()).apply { + this.timeZone = tz + } + private val otherYearSdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).apply { + this.timeZone = tz + } + private val otherYearCompleteSdf = SimpleDateFormat( + "yyyy-MM-dd HH:mm", + Locale.getDefault() + ).apply { + this.timeZone = tz + } @JvmOverloads fun format(time: Date?, shortFormat: Boolean = true, now: Date = Date()): String { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CryptoUtil.kt b/app/src/main/java/com/keylesspalace/tusky/util/CryptoUtil.kt index f4fa4b5ba..d2fe7d570 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/CryptoUtil.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/CryptoUtil.kt @@ -16,13 +16,13 @@ package com.keylesspalace.tusky.util import android.util.Base64 +import java.security.KeyPairGenerator +import java.security.SecureRandom +import java.security.Security import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.interfaces.ECPrivateKey import org.bouncycastle.jce.interfaces.ECPublicKey import org.bouncycastle.jce.provider.BouncyCastleProvider -import java.security.KeyPairGenerator -import java.security.SecureRandom -import java.security.Security object CryptoUtil { const val CURVE_PRIME256_V1 = "prime256v1" diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt index bd5facb73..8be617389 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/CustomEmojiHelper.kt @@ -56,7 +56,13 @@ fun CharSequence.emojify(emojis: List?, view: View, animate: Boolean): Ch builder.setSpan(span, matcher.start(), matcher.end(), 0) Glide.with(view) .asDrawable() - .load(if (animate) { url } else { staticUrl }) + .load( + if (animate) { + url + } else { + staticUrl + } + ) .into(span.getTarget(animate)) } } @@ -66,7 +72,13 @@ fun CharSequence.emojify(emojis: List?, view: View, animate: Boolean): Ch class EmojiSpan(val viewWeakReference: WeakReference) : ReplacementSpan() { var imageDrawable: Drawable? = null - override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int { + override fun getSize( + paint: Paint, + text: CharSequence, + start: Int, + end: Int, + fm: Paint.FontMetricsInt? + ): Int { if (fm != null) { /* update FontMetricsInt or otherwise span does not get drawn when * it covers the whole text */ @@ -80,7 +92,17 @@ class EmojiSpan(val viewWeakReference: WeakReference) : ReplacementSpan() return (paint.textSize * 1.2).toInt() } - override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { + override fun draw( + canvas: Canvas, + text: CharSequence, + start: Int, + end: Int, + x: Float, + top: Int, + y: Int, + bottom: Int, + paint: Paint + ) { imageDrawable?.let { drawable -> canvas.save() diff --git a/app/src/main/java/com/keylesspalace/tusky/util/EmptyPagingSource.kt b/app/src/main/java/com/keylesspalace/tusky/util/EmptyPagingSource.kt index 41a12aa3d..d08a9c129 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/EmptyPagingSource.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/EmptyPagingSource.kt @@ -6,5 +6,9 @@ import androidx.paging.PagingState class EmptyPagingSource : PagingSource() { override fun getRefreshKey(state: PagingState): Int? = null - override suspend fun load(params: LoadParams): LoadResult = LoadResult.Page(emptyList(), null, null) + override suspend fun load(params: LoadParams): LoadResult = LoadResult.Page( + emptyList(), + null, + null + ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/FlowExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/FlowExtensions.kt index 8de7129ce..54eaa8a23 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/FlowExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/FlowExtensions.kt @@ -17,11 +17,11 @@ package com.keylesspalace.tusky.util -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow import kotlin.time.Duration import kotlin.time.TimeMark import kotlin.time.TimeSource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow /** * Returns a flow that mirrors the original flow, but filters out values that occur within @@ -53,15 +53,13 @@ import kotlin.time.TimeSource * @param timeout Emissions within this duration of the last emission are filtered * @param timeSource Used to measure elapsed time. Normally only overridden in tests */ -fun Flow.throttleFirst( - timeout: Duration, - timeSource: TimeSource = TimeSource.Monotonic -) = flow { - var marker: TimeMark? = null - collect { - if (marker == null || marker!!.elapsedNow() >= timeout) { - emit(it) - marker = timeSource.markNow() +fun Flow.throttleFirst(timeout: Duration, timeSource: TimeSource = TimeSource.Monotonic) = + flow { + var marker: TimeMark? = null + collect { + if (marker == null || marker!!.elapsedNow() >= timeout) { + emit(it) + marker = timeSource.markNow() + } } } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt b/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt index 41d1034c7..d26d70cef 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/FocalPointUtil.kt @@ -144,12 +144,7 @@ object FocalPointUtil { * the image. So it won't put the very edge of the image in center, because that would * leave part of the view empty. */ - fun focalOffset( - view: Float, - image: Float, - scale: Float, - focal: Float - ): Float { + fun focalOffset(view: Float, image: Float, scale: Float, focal: Float): Float { // The fraction of the image that will be in view: val inView = view / (scale * image) var offset = 0f diff --git a/app/src/main/java/com/keylesspalace/tusky/util/HttpHeaderLink.kt b/app/src/main/java/com/keylesspalace/tusky/util/HttpHeaderLink.kt index a5ccd35be..8b902b518 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/HttpHeaderLink.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/HttpHeaderLink.kt @@ -123,10 +123,7 @@ constructor( * @param relationType of the parameter "rel", commonly "next" or "prev" * @return the link matching the given relation type */ - fun findByRelationType( - links: List, - relationType: String - ): HttpHeaderLink? { + fun findByRelationType(links: List, relationType: String): HttpHeaderLink? { return links.find { link -> link.parameters.any { parameter -> parameter.name == "rel" && parameter.value == relationType diff --git a/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.kt index 3b5c49adb..a955af255 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/IOUtils.kt @@ -36,10 +36,7 @@ fun Closeable?.closeQuietly() { } @SuppressLint("Recycle") // The linter can't tell that the stream gets closed by a helper method -fun Uri.copyToFile( - contentResolver: ContentResolver, - file: File -): Boolean { +fun Uri.copyToFile(contentResolver: ContentResolver, file: File): Boolean { val from: InputStream? val to: FileOutputStream diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt index a39d1a2c9..9ee165222 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt @@ -71,7 +71,13 @@ fun getDomain(urlString: String?): String { * @param mentions any '@' mentions which are known to be in the content * @param listener to notify about particular spans that are clicked */ -fun setClickableText(view: TextView, content: CharSequence, mentions: List, tags: List?, listener: LinkListener) { +fun setClickableText( + view: TextView, + content: CharSequence, + mentions: List, + tags: List?, + listener: LinkListener +) { val spannableContent = markupHiddenUrls(view, content) view.text = spannableContent.apply { @@ -93,7 +99,10 @@ fun markupHiddenUrls(view: TextView, content: CharSequence): SpannableStringBuil return@filter if (firstCharacter == '#' || firstCharacter == '@') { false } else { - val text = spannableContent.subSequence(start, spannableContent.getSpanEnd(it)).toString() + val text = spannableContent.subSequence( + start, + spannableContent.getSpanEnd(it) + ).toString() .split(' ').lastOrNull().orEmpty() var textDomain = getDomain(text) if (textDomain.isBlank()) { @@ -107,8 +116,16 @@ fun markupHiddenUrls(view: TextView, content: CharSequence): SpannableStringBuil val start = spannableContent.getSpanStart(span) val end = spannableContent.getSpanEnd(span) val originalText = spannableContent.subSequence(start, end) - val replacementText = view.context.getString(R.string.url_domain_notifier, originalText, getDomain(span.url)) - spannableContent.replace(start, end, replacementText) // this also updates the span locations + val replacementText = view.context.getString( + R.string.url_domain_notifier, + originalText, + getDomain(span.url) + ) + spannableContent.replace( + start, + end, + replacementText + ) // this also updates the span locations val linkDrawable = AppCompatResources.getDrawable(view.context, R.drawable.ic_link)!! // ImageSpan does not always align the icon correctly in the line, let's use our custom emoji span for this @@ -162,7 +179,12 @@ fun getTagName(text: CharSequence, tags: List?): String? { } } -private fun getCustomSpanForTag(text: CharSequence, tags: List?, span: URLSpan, listener: LinkListener): ClickableSpan? { +private fun getCustomSpanForTag( + text: CharSequence, + tags: List?, + span: URLSpan, + listener: LinkListener +): ClickableSpan? { return getTagName(text, tags)?.let { object : NoUnderlineURLSpan(span.url) { override fun onClick(view: View) = listener.onViewTag(it) @@ -170,14 +192,22 @@ private fun getCustomSpanForTag(text: CharSequence, tags: List?, span: } } -private fun getCustomSpanForMention(mentions: List, span: URLSpan, listener: LinkListener): ClickableSpan? { +private fun getCustomSpanForMention( + mentions: List, + span: URLSpan, + listener: LinkListener +): ClickableSpan? { // https://github.com/tuskyapp/Tusky/pull/2339 return mentions.firstOrNull { it.url == span.url }?.let { getCustomSpanForMentionUrl(span.url, it.id, listener) } } -private fun getCustomSpanForMentionUrl(url: String, mentionId: String, listener: LinkListener): ClickableSpan { +private fun getCustomSpanForMentionUrl( + url: String, + mentionId: String, + listener: LinkListener +): ClickableSpan { return object : MentionSpan(url) { override fun onClick(view: View) = listener.onViewAccount(mentionId) } @@ -264,7 +294,9 @@ fun createClickableText(text: String, link: String): CharSequence { */ fun Context.openLink(url: String) { val uri = url.toUri().normalizeScheme() - val useCustomTabs = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("customTabs", false) + val useCustomTabs = PreferenceManager.getDefaultSharedPreferences( + this + ).getBoolean("customTabs", false) if (useCustomTabs) { openLinkInCustomTab(uri, this) @@ -296,9 +328,21 @@ private fun openLinkInBrowser(uri: Uri?, context: Context) { * @param context context */ fun openLinkInCustomTab(uri: Uri, context: Context) { - val toolbarColor = MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurface, Color.BLACK) - val navigationbarColor = MaterialColors.getColor(context, android.R.attr.navigationBarColor, Color.BLACK) - val navigationbarDividerColor = MaterialColors.getColor(context, R.attr.dividerColor, Color.BLACK) + val toolbarColor = MaterialColors.getColor( + context, + com.google.android.material.R.attr.colorSurface, + Color.BLACK + ) + val navigationbarColor = MaterialColors.getColor( + context, + android.R.attr.navigationBarColor, + Color.BLACK + ) + val navigationbarDividerColor = MaterialColors.getColor( + context, + R.attr.dividerColor, + Color.BLACK + ) val colorSchemeParams = CustomTabColorSchemeParams.Builder() .setToolbarColor(toolbarColor) .setNavigationBarColor(navigationbarColor) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt index 077ec0085..118cb01fd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt @@ -94,11 +94,7 @@ class ListStatusAccessibilityDelegate( } } - override fun performAccessibilityAction( - host: View, - action: Int, - args: Bundle? - ): Boolean { + override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { val pos = recyclerView.getChildAdapterPosition(host) when (action) { R.id.action_reply -> { @@ -114,7 +110,11 @@ class ListStatusAccessibilityDelegate( R.id.action_open_profile -> { interrupt() statusActionListener.onViewAccount( - (statusProvider.getStatus(pos) as StatusViewData.Concrete).actionable.account.id + ( + statusProvider.getStatus( + pos + ) as StatusViewData.Concrete + ).actionable.account.id ) } R.id.action_open_media_1 -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/LocaleUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/LocaleUtils.kt index 655348b4d..3ad6c1dae 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/LocaleUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/LocaleUtils.kt @@ -59,7 +59,10 @@ private fun ensureLanguagesAreFirst(locales: MutableList, languages: Lis } } -fun getInitialLanguages(language: String? = null, activeAccount: AccountEntity? = null): List { +fun getInitialLanguages( + language: String? = null, + activeAccount: AccountEntity? = null +): List { val selected = listOfNotNull(language, activeAccount?.defaultPostLanguage) val system = AppCompatDelegate.getApplicationLocales().toList() + LocaleListCompat.getDefault().toList() diff --git a/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.kt index b01200bb5..84404bcc8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/MediaUtils.kt @@ -165,7 +165,10 @@ fun getImageOrientation(uri: Uri, contentResolver: ContentResolver): Int { inputStream.closeQuietly() return ExifInterface.ORIENTATION_UNDEFINED } - val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) + val orientation = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ) inputStream.closeQuietly() return orientation } @@ -196,5 +199,8 @@ fun deleteStaleCachedMedia(mediaDirectory: File?) { } fun getTemporaryMediaFilename(extension: String): String { - return "${MEDIA_TEMP_PREFIX}_${SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())}.$extension" + return "${MEDIA_TEMP_PREFIX}_${SimpleDateFormat( + "yyyyMMdd_HHmmss", + Locale.US + ).format(Date())}.$extension" } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/PairedList.kt b/app/src/main/java/com/keylesspalace/tusky/util/PairedList.kt index 39a47cc70..bdcf23925 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/PairedList.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/PairedList.kt @@ -26,7 +26,7 @@ import androidx.arch.core.util.Function * supplementary one. * @constructor */ -class PairedList (private val mapper: Function) : AbstractMutableList() { +class PairedList(private val mapper: Function) : AbstractMutableList() { private val main: MutableList = ArrayList() private val synced: MutableList = ArrayList() diff --git a/app/src/main/java/com/keylesspalace/tusky/util/Resource.kt b/app/src/main/java/com/keylesspalace/tusky/util/Resource.kt index ddc88f45d..c04eb05c2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/Resource.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/Resource.kt @@ -2,11 +2,11 @@ package com.keylesspalace.tusky.util sealed class Resource(open val data: T?) -class Loading (override val data: T? = null) : Resource(data) +class Loading(override val data: T? = null) : Resource(data) -class Success (override val data: T? = null) : Resource(data) +class Success(override val data: T? = null) : Resource(data) -class Error ( +class Error( override val data: T? = null, val errorMessage: String? = null, var consumed: Boolean = false, diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ShareShortcutHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/ShareShortcutHelper.kt index 800671ead..6a13a851c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ShareShortcutHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ShareShortcutHelper.kt @@ -57,7 +57,12 @@ fun updateShortcut(context: Context, account: AccountEntity) { val outBmp = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888) val canvas = Canvas(outBmp) - canvas.drawBitmap(bmp, (outerSize - innerSize).toFloat() / 2f, (outerSize - innerSize).toFloat() / 2f, null) + canvas.drawBitmap( + bmp, + (outerSize - innerSize).toFloat() / 2f, + (outerSize - innerSize).toFloat() / 2f, + null + ) val icon = IconCompat.createWithAdaptiveBitmap(outBmp) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt index 9a9c19bfc..c5e7e1a08 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt @@ -55,7 +55,14 @@ fun shouldTrimStatus(message: Spanned): Boolean { */ object SmartLengthInputFilter : InputFilter { /** {@inheritDoc} */ - override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? { + override fun filter( + source: CharSequence, + start: Int, + end: Int, + dest: Spanned, + dstart: Int, + dend: Int + ): CharSequence? { // Code originally imported from InputFilter.LengthFilter but heavily customized and converted to Kotlin. // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/InputFilter.java#175 diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.kt index 66cda17a5..faa671a3c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/SpanUtils.kt @@ -35,14 +35,17 @@ private const val HTTPS_URL_REGEX = "(?:(^|\\b)https://[^\\s]+)" /** * Dump of android.util.Patterns.WEB_URL */ -private val STRICT_WEB_URL_PATTERN = Pattern.compile("(((?:(?i:http|https|rtsp)://(?:(?:[a-zA-Z0-9\\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?(?:(([a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]](?:[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]_\\-]{0,61}[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]]){0,1}\\.)+(xn\\-\\-[\\w\\-]{0,58}\\w|[a-zA-Z[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]]{2,63})|((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9]))))(?:\\:\\d{1,5})?)([/\\?](?:(?:[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]];/\\?:@&=#~\\-\\.\\+!\\*'\\(\\),_\\\$])|(?:%[a-fA-F0-9]{2}))*)?(?:\\b|\$|^))") +private val STRICT_WEB_URL_PATTERN = Pattern.compile( + "(((?:(?i:http|https|rtsp)://(?:(?:[a-zA-Z0-9\\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?(?:(([a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]](?:[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]_\\-]{0,61}[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]]){0,1}\\.)+(xn\\-\\-[\\w\\-]{0,58}\\w|[a-zA-Z[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]]]{2,63})|((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9]))))(?:\\:\\d{1,5})?)([/\\?](?:(?:[a-zA-Z0-9[ -\uD7FF豈-\uFDCFﷰ-\uFFEF\uD800\uDC00-\uD83F\uDFFD\uD840\uDC00-\uD87F\uDFFD\uD880\uDC00-\uD8BF\uDFFD\uD8C0\uDC00-\uD8FF\uDFFD\uD900\uDC00-\uD93F\uDFFD\uD940\uDC00-\uD97F\uDFFD\uD980\uDC00-\uD9BF\uDFFD\uD9C0\uDC00-\uD9FF\uDFFD\uDA00\uDC00-\uDA3F\uDFFD\uDA40\uDC00-\uDA7F\uDFFD\uDA80\uDC00-\uDABF\uDFFD\uDAC0\uDC00-\uDAFF\uDFFD\uDB00\uDC00-\uDB3F\uDFFD\uDB44\uDC00-\uDB7F\uDFFD&&[^ [ - ]\u2028\u2029  ]];/\\?:@&=#~\\-\\.\\+!\\*'\\(\\),_\\\$])|(?:%[a-fA-F0-9]{2}))*)?(?:\\b|\$|^))" +) private val spanClasses = listOf(ForegroundColorSpan::class.java, URLSpan::class.java) private val finders = mapOf( FoundMatchType.HTTP_URL to PatternFinder(':', HTTP_URL_REGEX, 5, Character::isWhitespace), FoundMatchType.HTTPS_URL to PatternFinder(':', HTTPS_URL_REGEX, 6, Character::isWhitespace), FoundMatchType.TAG to PatternFinder('#', TAG_REGEX, 1, ::isValidForTagPrefix), - FoundMatchType.MENTION to PatternFinder('@', MENTION_REGEX, 1, Character::isWhitespace) // TODO: We also need a proper validator for mentions + // TODO: We also need a proper validator for mentions + FoundMatchType.MENTION to PatternFinder('@', MENTION_REGEX, 1, Character::isWhitespace) ) private enum class FoundMatchType { @@ -87,7 +90,12 @@ fun highlightSpans(text: Spannable, colour: Int) { start = found.start end = found.end if (start in 0 until end) { - text.setSpan(getSpan(found.matchType, string, colour, start, end), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE) + text.setSpan( + getSpan(found.matchType, string, colour, start, end), + start, + end, + Spanned.SPAN_INCLUSIVE_EXCLUSIVE + ) start += finders[found.matchType]!!.searchPrefixWidth } } @@ -181,7 +189,13 @@ private fun findEndOfPattern(string: String, result: FindCharsResult, pattern: P } } -private fun getSpan(matchType: FoundMatchType, string: String, colour: Int, start: Int, end: Int): CharacterStyle { +private fun getSpan( + matchType: FoundMatchType, + string: String, + colour: Int, + start: Int, + end: Int +): CharacterStyle { return when (matchType) { FoundMatchType.HTTP_URL -> NoUnderlineURLSpan(string.substring(start, end)) FoundMatchType.HTTPS_URL -> NoUnderlineURLSpan(string.substring(start, end)) diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt index 7151f93ac..e55e1706d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusDisplayOptions.kt @@ -53,11 +53,7 @@ data class StatusDisplayOptions( /** * @return a new StatusDisplayOptions adapted to whichever preference changed. */ - fun make( - preferences: SharedPreferences, - key: String, - account: AccountEntity - ) = when (key) { + fun make(preferences: SharedPreferences, key: String, account: AccountEntity) = when (key) { PrefKeys.ANIMATE_GIF_AVATARS -> copy( animateAvatars = preferences.getBoolean(key, false) ) @@ -91,7 +87,9 @@ data class StatusDisplayOptions( PrefKeys.ALWAYS_OPEN_SPOILER -> copy( openSpoiler = account.alwaysOpenSpoiler ) - else -> { this } + else -> { + this + } } companion object { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt index 2148a3b4c..db56aea4f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt @@ -68,7 +68,9 @@ class StatusViewHelper(private val itemView: View) { itemView.findViewById(R.id.status_media_overlay_3) ) - val sensitiveMediaWarning = itemView.findViewById(R.id.status_sensitive_media_warning) + val sensitiveMediaWarning = itemView.findViewById( + R.id.status_sensitive_media_warning + ) val sensitiveMediaShow = itemView.findViewById(R.id.status_sensitive_media_button) val mediaLabel = itemView.findViewById(R.id.status_media_label) if (statusDisplayOptions.mediaPreviewEnabled) { @@ -86,7 +88,10 @@ class StatusViewHelper(private val itemView: View) { return } - val mediaPreviewUnloaded = ColorDrawable(MaterialColors.getColor(context, R.attr.colorBackgroundAccent, Color.BLACK)) + val mediaPreviewUnloaded = + ColorDrawable( + MaterialColors.getColor(context, R.attr.colorBackgroundAccent, Color.BLACK) + ) val n = min(attachments.size, Status.MAX_MEDIA_ATTACHMENTS) @@ -246,7 +251,9 @@ class StatusViewHelper(private val itemView: View) { private fun getLabelTypeText(context: Context, type: Attachment.Type): String { return when (type) { Attachment.Type.IMAGE -> context.getString(R.string.post_media_images) - Attachment.Type.GIFV, Attachment.Type.VIDEO -> context.getString(R.string.post_media_video) + Attachment.Type.GIFV, Attachment.Type.VIDEO -> context.getString( + R.string.post_media_video + ) Attachment.Type.AUDIO -> context.getString(R.string.post_media_audio) else -> context.getString(R.string.post_media_attachments) } @@ -262,7 +269,11 @@ class StatusViewHelper(private val itemView: View) { } } - fun setupPollReadonly(poll: PollViewData?, emojis: List, statusDisplayOptions: StatusDisplayOptions) { + fun setupPollReadonly( + poll: PollViewData?, + emojis: List, + statusDisplayOptions: StatusDisplayOptions + ) { val pollResults = listOf( itemView.findViewById(R.id.status_poll_option_result_0), itemView.findViewById(R.id.status_poll_option_result_1), @@ -287,7 +298,12 @@ class StatusViewHelper(private val itemView: View) { } } - private fun getPollInfoText(timestamp: Long, poll: PollViewData, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence { + private fun getPollInfoText( + timestamp: Long, + poll: PollViewData, + pollDescription: TextView, + useAbsoluteTime: Boolean + ): CharSequence { val context = pollDescription.context val votesText = if (poll.votersCount == null) { @@ -301,7 +317,10 @@ class StatusViewHelper(private val itemView: View) { context.getString(R.string.poll_info_closed) } else { if (useAbsoluteTime) { - context.getString(R.string.poll_info_time_absolute, absoluteTimeFormatter.format(poll.expiresAt, false)) + context.getString( + R.string.poll_info_time_absolute, + absoluteTimeFormatter.format(poll.expiresAt, false) + ) } else { formatPollDuration(context, poll.expiresAt!!.time, timestamp) } @@ -310,14 +329,26 @@ class StatusViewHelper(private val itemView: View) { return context.getString(R.string.poll_info_format, votesText, pollDurationInfo) } - private fun setupPollResult(poll: PollViewData, emojis: List, pollResults: List, animateEmojis: Boolean) { + private fun setupPollResult( + poll: PollViewData, + emojis: List, + pollResults: List, + animateEmojis: Boolean + ) { val options = poll.options for (i in 0 until Status.MAX_POLL_OPTIONS) { if (i < options.size) { - val percent = calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount) + val percent = + calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount) - val pollOptionText = buildDescription(options[i].title, percent, options[i].voted, pollResults[i].context) + val pollOptionText = + buildDescription( + options[i].title, + percent, + options[i].voted, + pollResults[i].context + ) pollResults[i].text = pollOptionText.emojify(emojis, pollResults[i], animateEmojis) pollResults[i].visibility = View.VISIBLE diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt index c837fc3ea..8cf894dab 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ThrowableExtensions.kt @@ -2,10 +2,10 @@ package com.keylesspalace.tusky.util import android.content.Context import com.keylesspalace.tusky.R +import java.io.IOException import org.json.JSONException import org.json.JSONObject import retrofit2.HttpException -import java.io.IOException /** * checks if this throwable indicates an error causes by a 4xx/5xx server response and diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt index f398e2b6a..86bca03f6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewBindingExtensions.kt @@ -42,11 +42,15 @@ class FragmentViewBindingDelegate( } override fun onCreate(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver) + fragment.viewLifecycleOwnerLiveData.observeForever( + viewLifecycleOwnerLiveDataObserver + ) } override fun onDestroy(owner: LifecycleOwner) { - fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver) + fragment.viewLifecycleOwnerLiveData.removeObserver( + viewLifecycleOwnerLiveDataObserver + ) } }) } @@ -59,7 +63,9 @@ class FragmentViewBindingDelegate( val lifecycle = fragment.viewLifecycleOwner.lifecycle if (!lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) { - throw IllegalStateException("Should not attempt to get bindings when Fragment views are destroyed.") + throw IllegalStateException( + "Should not attempt to get bindings when Fragment views are destroyed." + ) } return viewBindingFactory(thisRef.requireView()).also { this.binding = it } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt index 17e814161..dc1ee9431 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt +++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.kt @@ -31,6 +31,7 @@ * * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ + package com.keylesspalace.tusky.util import androidx.paging.CombinedLoadStates diff --git a/app/src/main/java/com/keylesspalace/tusky/view/ClickableSpanTextView.kt b/app/src/main/java/com/keylesspalace/tusky/view/ClickableSpanTextView.kt index 0f1097155..a5d65716f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/ClickableSpanTextView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/ClickableSpanTextView.kt @@ -254,7 +254,10 @@ class ClickableSpanTextView @JvmOverloads constructor( activeEntry = entry continue } - Log.v(TAG, "Overlap: ${(entry.value as URLSpan).url} ${(activeEntry.value as URLSpan).url}") + Log.v( + TAG, + "Overlap: ${(entry.value as URLSpan).url} ${(activeEntry.value as URLSpan).url}" + ) if (isClickOnFirst(entry.key, activeEntry.key, x, y)) { activeEntry = entry } diff --git a/app/src/main/java/com/keylesspalace/tusky/view/LicenseCard.kt b/app/src/main/java/com/keylesspalace/tusky/view/LicenseCard.kt index 394cd3692..1b232eae1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/LicenseCard.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/LicenseCard.kt @@ -37,7 +37,13 @@ class LicenseCard init { val binding = CardLicenseBinding.inflate(LayoutInflater.from(context), this) - setCardBackgroundColor(MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurface, Color.BLACK)) + setCardBackgroundColor( + MaterialColors.getColor( + context, + com.google.android.material.R.attr.colorSurface, + Color.BLACK + ) + ) val (name, license, link) = context.theme.obtainStyledAttributes( attrs, diff --git a/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt b/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt index cf400a8f2..b68f4b7ad 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/MediaPreviewImageView.kt @@ -96,11 +96,22 @@ open class MediaPreviewImageView } } - override fun onLoadFailed(e: GlideException?, model: Any?, target: Target, isFirstResource: Boolean): Boolean { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { return false } - override fun onResourceReady(resource: Drawable, model: Any, target: Target?, dataSource: DataSource, isFirstResource: Boolean): Boolean { + override fun onResourceReady( + resource: Drawable, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { recalculateMatrix(width, height, resource) return false } diff --git a/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt b/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt index 12b0b990e..f8ac67452 100644 --- a/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt +++ b/app/src/main/java/com/keylesspalace/tusky/view/SliderPreference.kt @@ -45,7 +45,7 @@ class SliderPreference @JvmOverloads constructor( * @see Slider.getValue * @see Slider.setValue */ - var value: Float = defaultValue + var value: Float = DEFAULT_VALUE get() = _value set(v) { val clamped = max(max(v, valueFrom), min(v, valueTo)) @@ -68,7 +68,7 @@ class SliderPreference @JvmOverloads constructor( * Format string to be applied to values before setting the summary. For more control set * [SliderPreference.formatter] */ - var format: String = defaultFormat + var format: String = DEFAULT_FORMAT /** * Function that will be used to format the summary. The default formatter formats using the @@ -96,13 +96,18 @@ class SliderPreference @JvmOverloads constructor( // preference layout to the right of the title and summary. layoutResource = R.layout.pref_slider - val a = context.obtainStyledAttributes(attrs, R.styleable.SliderPreference, defStyleAttr, defStyleRes) + val a = context.obtainStyledAttributes( + attrs, + R.styleable.SliderPreference, + defStyleAttr, + defStyleRes + ) - value = a.getFloat(R.styleable.SliderPreference_android_value, defaultValue) - valueFrom = a.getFloat(R.styleable.SliderPreference_android_valueFrom, defaultValueFrom) - valueTo = a.getFloat(R.styleable.SliderPreference_android_valueTo, defaultValueTo) - stepSize = a.getFloat(R.styleable.SliderPreference_android_stepSize, defaultStepSize) - format = a.getString(R.styleable.SliderPreference_format) ?: defaultFormat + value = a.getFloat(R.styleable.SliderPreference_android_value, DEFAULT_VALUE) + valueFrom = a.getFloat(R.styleable.SliderPreference_android_valueFrom, DEFAULT_VALUE_FROM) + valueTo = a.getFloat(R.styleable.SliderPreference_android_valueTo, DEFAULT_VALUE_TO) + stepSize = a.getFloat(R.styleable.SliderPreference_android_stepSize, DEFAULT_STEP_SIZE) + format = a.getString(R.styleable.SliderPreference_format) ?: DEFAULT_FORMAT val decrementIconResource = a.getResourceId(R.styleable.SliderPreference_iconStart, -1) if (decrementIconResource != -1) { @@ -118,11 +123,11 @@ class SliderPreference @JvmOverloads constructor( } override fun onGetDefaultValue(a: TypedArray, i: Int): Any { - return a.getFloat(i, defaultValue) + return a.getFloat(i, DEFAULT_VALUE) } override fun onSetInitialValue(defaultValue: Any?) { - value = getPersistedFloat((defaultValue ?: Companion.defaultValue) as Float) + value = getPersistedFloat((defaultValue ?: DEFAULT_VALUE) as Float) } override fun onBindViewHolder(holder: PreferenceViewHolder) { @@ -179,10 +184,10 @@ class SliderPreference @JvmOverloads constructor( companion object { private const val TAG = "SliderPreference" - private const val defaultValueFrom = 0F - private const val defaultValueTo = 1F - private const val defaultValue = 0.5F - private const val defaultStepSize = 0.1F - private const val defaultFormat = "%3.1f" + private const val DEFAULT_VALUE_FROM = 0F + private const val DEFAULT_VALUE_TO = 1F + private const val DEFAULT_VALUE = 0.5F + private const val DEFAULT_STEP_SIZE = 0.1F + private const val DEFAULT_FORMAT = "%3.1f" } } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/AttachmentViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/AttachmentViewData.kt index 998cc96a4..2626cfeef 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/AttachmentViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/AttachmentViewData.kt @@ -35,7 +35,10 @@ data class AttachmentViewData( companion object { @JvmStatic - fun list(status: Status, alwaysShowSensitiveMedia: Boolean = false): List { + fun list( + status: Status, + alwaysShowSensitiveMedia: Boolean = false + ): List { val actionable = status.actionableStatus return actionable.attachments.map { attachment -> AttachmentViewData( diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt index 7281bf060..ce45dd414 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt @@ -53,7 +53,10 @@ fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int { } fun buildDescription(title: String, percent: Int, voted: Boolean, context: Context): Spanned { - val builder = SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml()) + val builder = + SpannableStringBuilder( + context.getString(R.string.poll_percent_format, percent).parseAsHtml() + ) if (voted) { builder.append(" ✓ ") } else { @@ -71,7 +74,11 @@ fun Poll?.toViewData(): PollViewData? { multiple = multiple, votesCount = votesCount, votersCount = votersCount, - options = options.mapIndexed { index, option -> option.toViewData(ownVotes?.contains(index) == true) }, + options = options.mapIndexed { index, option -> + option.toViewData( + ownVotes?.contains(index) == true + ) + }, voted = voted ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt index aafe4ce05..4765bd849 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt @@ -26,12 +26,15 @@ import com.keylesspalace.tusky.util.Either import com.keylesspalace.tusky.util.Either.Left import com.keylesspalace.tusky.util.Either.Right import com.keylesspalace.tusky.util.withoutFirstWhich +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch -import javax.inject.Inject -data class State(val accounts: Either>, val searchResult: List?) +data class State( + val accounts: Either>, + val searchResult: List? +) class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() { diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt index 55de04be4..5cbc0e57d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/EditProfileViewModel.kt @@ -35,6 +35,8 @@ import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.getServerErrorMessage import com.keylesspalace.tusky.util.randomAlphanumericString +import java.io.File +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asFlow @@ -44,8 +46,6 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody -import java.io.File -import javax.inject.Inject private const val HEADER_FILE_NAME = "header.png" private const val AVATAR_FILE_NAME = "avatar.png" @@ -119,12 +119,20 @@ class EditProfileViewModel @Inject constructor( viewModelScope.launch { var avatarFileBody: MultipartBody.Part? = null diff.avatarFile?.let { - avatarFileBody = MultipartBody.Part.createFormData("avatar", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + avatarFileBody = MultipartBody.Part.createFormData( + "avatar", + randomAlphanumericString(12), + it.asRequestBody("image/png".toMediaTypeOrNull()) + ) } var headerFileBody: MultipartBody.Part? = null diff.headerFile?.let { - headerFileBody = MultipartBody.Part.createFormData("header", randomAlphanumericString(12), it.asRequestBody("image/png".toMediaTypeOrNull())) + headerFileBody = MultipartBody.Part.createFormData( + "header", + randomAlphanumericString(12), + it.asRequestBody("image/png".toMediaTypeOrNull()) + ) } mastodonApi.accountUpdateCredentials( @@ -156,7 +164,10 @@ class EditProfileViewModel @Inject constructor( // cache activity state for rotation change internal fun updateProfile(newProfileData: ProfileDataInUi) { if (profileData.value is Success) { - val newProfileSource = profileData.value?.data?.source?.copy(note = newProfileData.note, fields = newProfileData.fields) + val newProfileSource = profileData.value?.data?.source?.copy( + note = newProfileData.note, + fields = newProfileData.fields + ) val newProfile = profileData.value?.data?.copy( displayName = newProfileData.displayName, locked = newProfileData.locked, @@ -173,7 +184,10 @@ class EditProfileViewModel @Inject constructor( return diff.hasChanges() } - private fun getProfileDiff(oldProfileAccount: Account?, newProfileData: ProfileDataInUi): DiffProfileData { + private fun getProfileDiff( + oldProfileAccount: Account?, + newProfileData: ProfileDataInUi + ): DiffProfileData { val displayName = if (oldProfileAccount?.displayName == newProfileData.displayName) { null } else { @@ -216,7 +230,10 @@ class EditProfileViewModel @Inject constructor( ) } - private fun calculateFieldToUpdate(newField: StringField?, fieldsUnchanged: Boolean): Pair? { + private fun calculateFieldToUpdate( + newField: StringField?, + fieldsUnchanged: Boolean + ): Pair? { if (fieldsUnchanged || newField == null) { return null } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt index 0dd05dafe..500500328 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt @@ -23,22 +23,28 @@ import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.replacedFirstWhich import com.keylesspalace.tusky.util.withoutFirstWhich +import java.io.IOException +import java.net.ConnectException +import javax.inject.Inject import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch -import java.io.IOException -import java.net.ConnectException -import javax.inject.Inject internal class ListsViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() { enum class LoadingState { - INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER + INITIAL, + LOADING, + LOADED, + ERROR_NETWORK, + ERROR_OTHER } enum class Event { - CREATE_ERROR, DELETE_ERROR, UPDATE_ERROR + CREATE_ERROR, + DELETE_ERROR, + UPDATE_ERROR } data class State(val lists: List, val loadingState: LoadingState) @@ -46,7 +52,12 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) val state: Flow get() = _state val events: Flow get() = _events private val _state = MutableStateFlow(State(listOf(), LoadingState.INITIAL)) - private val _events = MutableSharedFlow(replay = 0, extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _events = + MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) fun retryLoading() { loadIfNeeded() diff --git a/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt b/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt index cc99b78b1..a7362630c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt +++ b/app/src/main/java/com/keylesspalace/tusky/worker/NotificationWorker.kt @@ -34,14 +34,20 @@ class NotificationWorker( params: WorkerParameters, private val notificationsFetcher: NotificationFetcher ) : CoroutineWorker(appContext, params) { - val notification: Notification = NotificationHelper.createWorkerNotification(applicationContext, R.string.notification_notification_worker) + val notification: Notification = NotificationHelper.createWorkerNotification( + applicationContext, + R.string.notification_notification_worker + ) override suspend fun doWork(): Result { notificationsFetcher.fetchAndShow() return Result.success() } - override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_FETCH_NOTIFICATION, notification) + override suspend fun getForegroundInfo() = ForegroundInfo( + NOTIFICATION_ID_FETCH_NOTIFICATION, + notification + ) class Factory @Inject constructor( private val notificationsFetcher: NotificationFetcher diff --git a/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt b/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt index 5a65a2ef9..c69a035a6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt +++ b/app/src/main/java/com/keylesspalace/tusky/worker/PruneCacheWorker.kt @@ -38,7 +38,10 @@ class PruneCacheWorker( private val appDatabase: AppDatabase, private val accountManager: AccountManager ) : CoroutineWorker(appContext, workerParams) { - val notification: Notification = NotificationHelper.createWorkerNotification(applicationContext, R.string.notification_prune_cache) + val notification: Notification = NotificationHelper.createWorkerNotification( + applicationContext, + R.string.notification_prune_cache + ) override suspend fun doWork(): Result { for (account in accountManager.accounts) { @@ -48,7 +51,10 @@ class PruneCacheWorker( return Result.success() } - override suspend fun getForegroundInfo() = ForegroundInfo(NOTIFICATION_ID_PRUNE_CACHE, notification) + override suspend fun getForegroundInfo() = ForegroundInfo( + NOTIFICATION_ID_PRUNE_CACHE, + notification + ) companion object { private const val TAG = "PruneCacheWorker" diff --git a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt index 18db7b63d..8e08b3f0f 100644 --- a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt @@ -24,6 +24,8 @@ import io.reactivex.rxjava3.android.plugins.RxAndroidPlugins import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.plugins.RxJavaPlugins import io.reactivex.rxjava3.schedulers.TestScheduler +import java.util.Date +import java.util.concurrent.TimeUnit import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -34,8 +36,6 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mockito.eq import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock -import java.util.Date -import java.util.concurrent.TimeUnit class BottomSheetActivityTest { diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterV1Test.kt b/app/src/test/java/com/keylesspalace/tusky/FilterV1Test.kt index a3e5c8979..2d2011eca 100644 --- a/app/src/test/java/com/keylesspalace/tusky/FilterV1Test.kt +++ b/app/src/test/java/com/keylesspalace/tusky/FilterV1Test.kt @@ -26,14 +26,14 @@ import com.keylesspalace.tusky.entity.Poll import com.keylesspalace.tusky.entity.PollOption import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.FilterModel +import java.time.Instant +import java.util.Date import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock import org.robolectric.annotation.Config -import java.time.Instant -import java.util.Date @Config(sdk = [28]) @RunWith(AndroidJUnit4::class) diff --git a/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt index 8ad20a411..c93a587f8 100644 --- a/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt @@ -16,6 +16,7 @@ import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.entity.TimelineAccount +import java.util.Date import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Before @@ -27,7 +28,6 @@ import org.robolectric.Robolectric import org.robolectric.Shadows.shadowOf import org.robolectric.android.util.concurrent.BackgroundExecutor.runInBackground import org.robolectric.annotation.Config -import java.util.Date @Config(sdk = [28]) @RunWith(AndroidJUnit4::class) diff --git a/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivityTest.kt index f46550e04..c331e7dd9 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/compose/ComposeActivityTest.kt @@ -37,6 +37,7 @@ import com.keylesspalace.tusky.entity.InstanceConfiguration import com.keylesspalace.tusky.entity.InstanceV1 import com.keylesspalace.tusky.entity.StatusConfiguration import com.keylesspalace.tusky.network.MastodonApi +import java.util.Locale import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.toResponseBody import org.junit.Assert.assertEquals @@ -55,7 +56,6 @@ import org.robolectric.annotation.Config import org.robolectric.fakes.RoboMenuItem import retrofit2.HttpException import retrofit2.Response -import java.util.Locale /** * Created by charlag on 3/7/18. @@ -548,7 +548,7 @@ class ComposeActivityTest { instanceResponseCallback = { getSampleFriendicaInstance() } setupActivity() shadowOf(getMainLooper()).idle() - assertEquals(friendicaMaximum, activity.maximumTootCharacters) + assertEquals(FRIENDICA_MAXIMUM, activity.maximumTootCharacters) } private fun clickUp() { @@ -585,7 +585,7 @@ class ComposeActivityTest { ), Instance.Configuration.MediaAttachments(0, 0, 0, 0, 0), Instance.Configuration.Polls(0, 0, 0, 0), - Instance.Configuration.Translation(false), + Instance.Configuration.Translation(false) ) } @@ -620,7 +620,7 @@ class ComposeActivityTest { } companion object { - private const val friendicaMaximum = 200000 + private const val FRIENDICA_MAXIMUM = 200000 // https://github.com/tuskyapp/Tusky/issues/4100 private val sampleFriendicaResponse = """{ @@ -642,7 +642,7 @@ class ComposeActivityTest { ], "configuration": { "statuses": { - "max_characters": $friendicaMaximum + "max_characters": $FRIENDICA_MAXIMUM }, "media_attachments": { "supported_mime_types": { diff --git a/app/src/test/java/com/keylesspalace/tusky/components/timeline/CachedTimelineRemoteMediatorTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/timeline/CachedTimelineRemoteMediatorTest.kt index ad7bffd38..1724d485c 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/timeline/CachedTimelineRemoteMediatorTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/timeline/CachedTimelineRemoteMediatorTest.kt @@ -17,6 +17,7 @@ import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.db.Converters import com.keylesspalace.tusky.db.TimelineStatusWithAccount +import java.io.IOException import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import okhttp3.ResponseBody.Companion.toResponseBody @@ -35,7 +36,6 @@ import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config import retrofit2.HttpException import retrofit2.Response -import java.io.IOException @Config(sdk = [28]) @RunWith(AndroidJUnit4::class) diff --git a/app/src/test/java/com/keylesspalace/tusky/components/timeline/NetworkTimelineRemoteMediatorTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/timeline/NetworkTimelineRemoteMediatorTest.kt index c36d74363..fe33c0933 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/timeline/NetworkTimelineRemoteMediatorTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/timeline/NetworkTimelineRemoteMediatorTest.kt @@ -13,6 +13,7 @@ import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.viewdata.StatusViewData +import java.io.IOException import kotlinx.coroutines.runBlocking import okhttp3.Headers import okhttp3.ResponseBody.Companion.toResponseBody @@ -28,7 +29,6 @@ import org.mockito.kotlin.verify import org.robolectric.annotation.Config import retrofit2.HttpException import retrofit2.Response -import java.io.IOException @Config(sdk = [29]) @RunWith(AndroidJUnit4::class) @@ -389,7 +389,7 @@ class NetworkTimelineRemoteMediatorTest { val statuses: MutableList = mutableListOf( mockStatusViewData("5"), mockStatusViewData("4"), - mockStatusViewData("3"), + mockStatusViewData("3") ) val timelineViewModel: NetworkTimelineViewModel = mock { @@ -400,11 +400,11 @@ class NetworkTimelineRemoteMediatorTest { listOf( mockStatus("3"), mockStatus("2"), - mockStatus("1"), + mockStatus("1") ), Headers.headersOf( "Link", - "; rel=\"next\"", + "; rel=\"next\"" ) ) } diff --git a/app/src/test/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModelTest.kt b/app/src/test/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModelTest.kt index b39765a5c..55b57b3b5 100644 --- a/app/src/test/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModelTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/components/viewthread/ViewThreadViewModelTest.kt @@ -19,6 +19,7 @@ import com.keylesspalace.tusky.entity.StatusContext import com.keylesspalace.tusky.network.FilterModel import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.usecase.TimelineCases +import java.io.IOException import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.junit.After @@ -32,7 +33,6 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.stub import org.robolectric.Shadows.shadowOf import org.robolectric.annotation.Config -import java.io.IOException @Config(sdk = [28]) @RunWith(AndroidJUnit4::class) diff --git a/app/src/test/java/com/keylesspalace/tusky/usecase/TimelineCasesTest.kt b/app/src/test/java/com/keylesspalace/tusky/usecase/TimelineCasesTest.kt index b31e37a3f..988cd6e44 100644 --- a/app/src/test/java/com/keylesspalace/tusky/usecase/TimelineCasesTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/usecase/TimelineCasesTest.kt @@ -7,6 +7,7 @@ import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.StatusChangedEvent import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi +import java.util.Date import kotlinx.coroutines.runBlocking import okhttp3.ResponseBody.Companion.toResponseBody import org.junit.Assert.assertEquals @@ -19,7 +20,6 @@ import org.mockito.kotlin.stub import org.robolectric.annotation.Config import retrofit2.HttpException import retrofit2.Response -import java.util.Date @Config(sdk = [28]) @RunWith(AndroidJUnit4::class) diff --git a/app/src/test/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatterTest.kt b/app/src/test/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatterTest.kt index bee697e36..3fc2ee474 100644 --- a/app/src/test/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatterTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/util/AbsoluteTimeFormatterTest.kt @@ -1,11 +1,11 @@ package com.keylesspalace.tusky.util +import java.time.Instant +import java.util.* import org.junit.AfterClass import org.junit.Assert.assertEquals import org.junit.BeforeClass import org.junit.Test -import java.time.Instant -import java.util.* class AbsoluteTimeFormatterTest { companion object { diff --git a/app/src/test/java/com/keylesspalace/tusky/util/FlowExtensionsTest.kt b/app/src/test/java/com/keylesspalace/tusky/util/FlowExtensionsTest.kt index 5cbb231b6..7aa8b0696 100644 --- a/app/src/test/java/com/keylesspalace/tusky/util/FlowExtensionsTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/util/FlowExtensionsTest.kt @@ -19,14 +19,14 @@ package com.keylesspalace.tusky.util import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.ExperimentalTime import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Test -import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.ExperimentalTime @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTime::class) class FlowExtensionsTest { diff --git a/app/src/test/java/com/keylesspalace/tusky/util/NumberUtilsTest.kt b/app/src/test/java/com/keylesspalace/tusky/util/NumberUtilsTest.kt index 25ac69ee1..6b821b1c6 100644 --- a/app/src/test/java/com/keylesspalace/tusky/util/NumberUtilsTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/util/NumberUtilsTest.kt @@ -1,13 +1,13 @@ package com.keylesspalace.tusky.util +import java.util.Locale +import kotlin.math.pow import org.junit.AfterClass import org.junit.Assert import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized -import java.util.Locale -import kotlin.math.pow @RunWith(Parameterized::class) class NumberUtilsTest(private val input: Long, private val want: String) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 17a52a664..642ba8282 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,7 +60,7 @@ android-application = { id = "com.android.application", version.ref = "agp" } google-ksp = "com.google.devtools.ksp:1.9.20-1.0.13" kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } -ktlint = "org.jlleitschuh.gradle.ktlint:11.5.1" +ktlint = "org.jlleitschuh.gradle.ktlint:12.0.3" [libraries] android-material = { module = "com.google.android.material:material", version.ref = "material" }