From d17a0c43ab3d4ade20742d892cacc1f6f5e29386 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Fri, 4 Nov 2022 19:22:38 +0100 Subject: [PATCH] Api 33 support (#2719) * update to Api 33, fix some deprecations * fix deprecated serializable/parcelable methods * ask for notification permission * fix code formatting * add back comment in PreferencesActivity --- app/build.gradle | 15 +++--- app/src/main/AndroidManifest.xml | 1 + .../tusky/AccountListActivity.kt | 3 +- .../com/keylesspalace/tusky/BaseActivity.java | 2 +- .../com/keylesspalace/tusky/MainActivity.kt | 46 +++++++++++------ .../tusky/TabPreferenceActivity.kt | 20 ++++---- .../keylesspalace/tusky/ViewMediaActivity.kt | 3 +- .../components/compose/ComposeActivity.kt | 50 +++++++++++-------- .../compose/dialog/CaptionDialog.kt | 4 +- .../components/login/LoginWebViewActivity.kt | 5 +- .../preference/PreferencesActivity.kt | 41 ++++++++------- .../fragments/SearchStatusesFragment.kt | 2 +- .../viewthread/ViewThreadFragment.kt | 2 +- .../tusky/fragment/AccountListFragment.kt | 7 +-- .../tusky/fragment/ViewImageFragment.kt | 3 +- .../tusky/fragment/ViewVideoFragment.kt | 3 +- .../receiver/SendStatusBroadcastReceiver.kt | 3 +- .../tusky/service/SendStatusService.kt | 3 +- .../tusky/util/CompatExtensions.kt | 49 ++++++++++++++++++ .../tusky/ComposeActivityTest.kt | 2 +- 20 files changed, 174 insertions(+), 90 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CompatExtensions.kt diff --git a/app/build.gradle b/app/build.gradle index d5ddf9b34..806f25c63 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,11 +19,11 @@ def getGitSha = { } android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { applicationId APP_ID minSdkVersion 23 - targetSdkVersion 31 + targetSdkVersion 33 versionCode 94 versionName "19.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -108,16 +108,17 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$coroutinesVersion" - implementation "androidx.core:core-ktx:1.8.0" - implementation "androidx.appcompat:appcompat:1.4.2" - implementation "androidx.fragment:fragment-ktx:1.5.1" + implementation "androidx.core:core-ktx:1.9.0" + implementation "androidx.appcompat:appcompat:1.5.1" + implementation "androidx.activity:activity-ktx:1.6.0" + implementation "androidx.fragment:fragment-ktx:1.5.3" implementation "androidx.browser:browser:1.4.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.recyclerview:recyclerview:1.2.1" - implementation "androidx.exifinterface:exifinterface:1.3.3" + implementation "androidx.exifinterface:exifinterface:1.3.4" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.preference:preference-ktx:1.2.0" - implementation "androidx.sharetarget:sharetarget:1.2.0-rc01" + implementation "androidx.sharetarget:sharetarget:1.2.0" implementation "androidx.emoji2:emoji2:$emoji2_version" implementation "androidx.emoji2:emoji2-views:$emoji2_version" implementation "androidx.emoji2:emoji2-views-helper:$emoji2_version" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5600bbe2d..659786e1b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ package="com.keylesspalace.tusky"> + diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.kt index ca23f7912..723a35193 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.kt @@ -20,6 +20,7 @@ import android.content.Intent import android.os.Bundle import com.keylesspalace.tusky.databinding.ActivityAccountListBinding import com.keylesspalace.tusky.fragment.AccountListFragment +import com.keylesspalace.tusky.util.requireSerializableExtra import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import javax.inject.Inject @@ -44,7 +45,7 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector { val binding = ActivityAccountListBinding.inflate(layoutInflater) setContentView(binding.root) - val type = intent.getSerializableExtra(EXTRA_TYPE) as Type + val type: Type = intent.requireSerializableExtra(EXTRA_TYPE) val id: String? = intent.getStringExtra(EXTRA_ID) val accountLocked: Boolean = intent.getBooleanExtra(EXTRA_ACCOUNT_LOCKED, false) diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index d34dd6df8..eaf2db8b9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -132,7 +132,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { - onBackPressed(); + getOnBackPressedDispatcher().onBackPressed(); return true; } return super.onOptionsItemSelected(item); diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt index bc3b2b688..80bcfde33 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.kt @@ -15,9 +15,11 @@ package com.keylesspalace.tusky +import android.Manifest import android.content.Context import android.content.DialogInterface import android.content.Intent +import android.content.pm.PackageManager import android.content.res.ColorStateList import android.graphics.Bitmap import android.graphics.Color @@ -31,8 +33,11 @@ import android.view.KeyEvent import android.view.MenuItem import android.view.View import android.widget.ImageView +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.view.GravityCompat import androidx.lifecycle.Lifecycle @@ -267,6 +272,33 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } selectedEmojiPack = preferences.getString(EMOJI_PREFERENCE, "") + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + when { + binding.mainDrawerLayout.isOpen -> { + binding.mainDrawerLayout.close() + } + binding.viewPager.currentItem != 0 -> { + binding.viewPager.currentItem = 0 + } + else -> { + finish() + } + } + } + } + ) + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.POST_NOTIFICATIONS), + 1 + ) + } } override fun onResume() { @@ -292,20 +324,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje } } - override fun onBackPressed() { - when { - binding.mainDrawerLayout.isOpen -> { - binding.mainDrawerLayout.close() - } - binding.viewPager.currentItem != 0 -> { - binding.viewPager.currentItem = 0 - } - else -> { - super.onBackPressed() - } - } - } - override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { when (keyCode) { KeyEvent.KEYCODE_MENU -> { diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt index 76418e019..0f20a7851 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt @@ -20,9 +20,9 @@ import android.os.Bundle import android.util.Log import android.view.View import android.widget.FrameLayout +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.AppCompatEditText -import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -74,6 +74,12 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene private val hashtagRegex by lazy { Pattern.compile("([\\w_]*[\\p{Alpha}_][\\w_]*)", Pattern.CASE_INSENSITIVE) } + private val onFabDismissedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + toggleFab(false) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -149,6 +155,8 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene binding.maxTabsInfo.text = resources.getQuantityString(R.plurals.max_tab_number_reached, MAX_TAB_COUNT, MAX_TAB_COUNT) updateAvailableTabs() + + onBackPressedDispatcher.addCallback(onFabDismissedCallback) } override fun onTabAdded(tab: TabData) { @@ -209,6 +217,8 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene binding.actionButton.visible(!expand) binding.sheet.visible(expand) binding.scrim.visible(expand) + + onFabDismissedCallback.isEnabled = expand } private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) { @@ -338,14 +348,6 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene tabsChanged = true } - override fun onBackPressed() { - if (binding.actionButton.isVisible) { - super.onBackPressed() - } else { - toggleFab(false) - } - } - override fun onPause() { super.onPause() if (tabsChanged) { diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt index 2a96cb7fe..94bc288a8 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt @@ -54,6 +54,7 @@ import com.keylesspalace.tusky.fragment.ViewImageFragment import com.keylesspalace.tusky.pager.ImagePagerAdapter import com.keylesspalace.tusky.pager.SingleImagePagerAdapter import com.keylesspalace.tusky.util.getTemporaryMediaFilename +import com.keylesspalace.tusky.util.parcelableArrayListExtra import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.viewdata.AttachmentViewData import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers @@ -94,7 +95,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener supportPostponeEnterTransition() // Gather the parameters. - attachments = intent.getParcelableArrayListExtra(EXTRA_ATTACHMENTS) + attachments = intent.parcelableArrayListExtra(EXTRA_ATTACHMENTS) val initialPosition = intent.getIntExtra(EXTRA_ATTACHMENT_INDEX, 0) // Adapter is actually of existential type PageAdapter & SharedElementsTransitionListener 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 8106c7ff1..cb2c2130b 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 @@ -40,6 +40,7 @@ import android.widget.ImageButton import android.widget.LinearLayout import android.widget.PopupMenu import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.annotation.ColorInt @@ -92,6 +93,8 @@ import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.highlightSpans import com.keylesspalace.tusky.util.loadAvatar import com.keylesspalace.tusky.util.onTextChanged +import com.keylesspalace.tusky.util.parcelableArrayListExtra +import com.keylesspalace.tusky.util.parcelableExtra import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.visible @@ -237,8 +240,7 @@ 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? = intent.getParcelableExtra(COMPOSE_OPTIONS_EXTRA) + val composeOptions: ComposeOptions? = intent.parcelableExtra(COMPOSE_OPTIONS_EXTRA) viewModel.setup(composeOptions) @@ -299,12 +301,12 @@ class ComposeActivity : if (type.startsWith("image/") || type.startsWith("video/") || type.startsWith("audio/")) { when (intent.action) { Intent.ACTION_SEND -> { - intent.getParcelableExtra(Intent.EXTRA_STREAM)?.let { uri -> + intent.parcelableExtra(Intent.EXTRA_STREAM)?.let { uri -> pickMedia(uri) } } Intent.ACTION_SEND_MULTIPLE -> { - intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)?.forEach { uri -> + intent.parcelableArrayListExtra(Intent.EXTRA_STREAM)?.forEach { uri -> pickMedia(uri) } } @@ -510,6 +512,27 @@ class ComposeActivity : binding.actionPhotoTake.setOnClickListener { initiateCameraApp() } binding.actionPhotoPick.setOnClickListener { onMediaPick() } binding.addPollTextActionTextView.setOnClickListener { openPollDialog() } + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_EXPANDED || + addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED || + emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED || + scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED + ) { + composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN + addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN + emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN + scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN + return + } + + handleCloseButton() + } + } + ) } private fun setupLanguageSpinner(initialLanguage: String?) { @@ -1069,23 +1092,6 @@ class ComposeActivity : return super.onOptionsItemSelected(item) } - override fun onBackPressed() { - // Acting like a teen: deliberately ignoring parent. - if (composeOptionsBehavior.state == BottomSheetBehavior.STATE_EXPANDED || - addMediaBehavior.state == BottomSheetBehavior.STATE_EXPANDED || - emojiBehavior.state == BottomSheetBehavior.STATE_EXPANDED || - scheduleBehavior.state == BottomSheetBehavior.STATE_EXPANDED - ) { - composeOptionsBehavior.state = BottomSheetBehavior.STATE_HIDDEN - addMediaBehavior.state = BottomSheetBehavior.STATE_HIDDEN - emojiBehavior.state = BottomSheetBehavior.STATE_HIDDEN - scheduleBehavior.state = BottomSheetBehavior.STATE_HIDDEN - return - } - - handleCloseButton() - } - override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { Log.d(TAG, event.toString()) if (event.action == KeyEvent.ACTION_DOWN) { @@ -1098,7 +1104,7 @@ class ComposeActivity : } if (keyCode == KeyEvent.KEYCODE_BACK) { - onBackPressed() + onBackPressedDispatcher.onBackPressed() return true } } 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 614b87eed..54fb645d6 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 @@ -38,6 +38,7 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.github.chrisbanes.photoview.PhotoView import com.keylesspalace.tusky.R +import com.keylesspalace.tusky.util.parcelable // https://github.com/tootsuite/mastodon/blob/c6904c0d3766a2ea8a81ab025c127169ecb51373/app/models/media_attachment.rb#L32 private const val MEDIA_DESCRIPTION_CHARACTER_LIMIT = 1500 @@ -93,8 +94,7 @@ class CaptionDialog : DialogFragment() { val window = dialog.window window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) - val previewUri = - arguments?.getParcelable(PREVIEW_URI_ARG) ?: error("Preview Uri is null") + val previewUri: Uri = arguments?.parcelable(PREVIEW_URI_ARG) ?: error("Preview Uri is null") // Load the image and manually set it into the ImageView because it doesn't have a fixed size. Glide.with(this) .load(previewUri) 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 07bd56529..b38e86f12 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 @@ -23,6 +23,7 @@ import com.keylesspalace.tusky.R import com.keylesspalace.tusky.databinding.ActivityLoginWebviewBinding import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.util.hide +import com.keylesspalace.tusky.util.parcelableExtra import com.keylesspalace.tusky.util.viewBinding import kotlinx.parcelize.Parcelize @@ -39,7 +40,7 @@ class OauthLogin : ActivityResultContract() { return if (resultCode == Activity.RESULT_CANCELED) { LoginResult.Cancel } else { - intent!!.getParcelableExtra(RESULT_EXTRA)!! + intent!!.parcelableExtra(RESULT_EXTRA)!! } } @@ -48,7 +49,7 @@ class OauthLogin : ActivityResultContract() { private const val DATA_EXTRA = "data" fun parseData(intent: Intent): LoginData { - return intent.getParcelableExtra(DATA_EXTRA)!! + return intent.parcelableExtra(DATA_EXTRA)!! } fun makeResultIntent(result: LoginResult): Intent { 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 8ae4e0560..878766fa9 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 @@ -20,6 +20,7 @@ import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.util.Log +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.fragment.app.commit import androidx.preference.PreferenceManager @@ -47,7 +48,17 @@ class PreferencesActivity : @Inject lateinit var androidInjector: DispatchingAndroidInjector - private var restartActivitiesOnExit: Boolean = false + private val restartActivitiesOnBackPressedCallback = object : OnBackPressedCallback(false) { + override fun handleOnBackPressed() { + /* Switching themes won't actually change the theme of activities on the back stack. + * Either the back stack activities need to all be recreated, or do the easier thing, which + * is hijack the back button press and use it to launch a new MainActivity and clear the + * back stack. */ + val intent = Intent(this@PreferencesActivity, MainActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivityWithSlideInAnimation(intent) + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -92,7 +103,8 @@ class PreferencesActivity : replace(R.id.fragment_container, fragment, fragmentTag) } - restartActivitiesOnExit = intent.getBooleanExtra("restart", false) + onBackPressedDispatcher.addCallback(this, restartActivitiesOnBackPressedCallback) + restartActivitiesOnBackPressedCallback.isEnabled = savedInstanceState?.getBoolean(EXTRA_RESTART_ON_BACK, false) ?: false } override fun onResume() { @@ -106,11 +118,11 @@ class PreferencesActivity : } private fun saveInstanceState(outState: Bundle) { - outState.putBoolean("restart", restartActivitiesOnExit) + outState.putBoolean(EXTRA_RESTART_ON_BACK, restartActivitiesOnBackPressedCallback.isEnabled) } override fun onSaveInstanceState(outState: Bundle) { - outState.putBoolean("restart", restartActivitiesOnExit) + outState.putBoolean(EXTRA_RESTART_ON_BACK, restartActivitiesOnBackPressedCallback.isEnabled) super.onSaveInstanceState(outState) } @@ -121,16 +133,16 @@ class PreferencesActivity : Log.d("activeTheme", theme) ThemeUtils.setAppNightMode(theme) - restartActivitiesOnExit = true + restartActivitiesOnBackPressedCallback.isEnabled = true this.restartCurrentActivity() } "statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", "useBlurhash", "showSelfUsername", "showCardsInTimelines", "confirmReblogs", "confirmFavourites", "enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR -> { - restartActivitiesOnExit = true + restartActivitiesOnBackPressedCallback.isEnabled = true } "language" -> { - restartActivitiesOnExit = true + restartActivitiesOnBackPressedCallback.isEnabled = true this.restartCurrentActivity() } } @@ -148,20 +160,6 @@ class PreferencesActivity : overridePendingTransition(R.anim.fade_in, R.anim.fade_out) } - override fun onBackPressed() { - /* Switching themes won't actually change the theme of activities on the back stack. - * Either the back stack activities need to all be recreated, or do the easier thing, which - * is hijack the back button press and use it to launch a new MainActivity and clear the - * back stack. */ - if (restartActivitiesOnExit) { - val intent = Intent(this, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - startActivityWithSlideInAnimation(intent) - } else { - super.onBackPressed() - } - } - override fun androidInjector() = androidInjector companion object { @@ -172,6 +170,7 @@ class PreferencesActivity : const val TAB_FILTER_PREFERENCES = 3 const val PROXY_PREFERENCES = 4 private const val EXTRA_PREFERENCE_TYPE = "EXTRA_PREFERENCE_TYPE" + private const val EXTRA_RESTART_ON_BACK = "restart" @JvmStatic fun newIntent(context: Context, preferenceType: Int): Intent { 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 a799bee3c..b15b9e0f3 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 @@ -383,7 +383,7 @@ class SearchStatusesFragment : SearchFragment(), Status } != null } - private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence) { + private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence?) { bottomSheetActivity?.showAccountChooserDialog( dialogTitle, false, object : AccountSelectionListener { 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 cee6f0462..9fe91b921 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 @@ -102,7 +102,7 @@ class ViewThreadFragment : SFragment(), OnRefreshListener, StatusActionListener, override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.toolbar.setNavigationOnClickListener { - activity?.onBackPressed() + activity?.onBackPressedDispatcher?.onBackPressed() } binding.toolbar.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt index 465b9f216..8cc855ce6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountListFragment.kt @@ -49,6 +49,7 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.hide +import com.keylesspalace.tusky.util.requireSerializable import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.view.EndlessOnScrollListener @@ -78,8 +79,8 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - type = arguments?.getSerializable(ARG_TYPE) as Type - id = arguments?.getString(ARG_ID) + type = requireArguments().requireSerializable(ARG_TYPE) + id = requireArguments().getString(ARG_ID) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -100,7 +101,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct Type.BLOCKS -> BlocksAdapter(this, animateAvatar, animateEmojis) Type.MUTES -> MutesAdapter(this, animateAvatar, animateEmojis) Type.FOLLOW_REQUESTS -> { - val headerAdapter = FollowRequestsHeaderAdapter(accountManager.activeAccount!!.domain, arguments?.get(ARG_ACCOUNT_LOCKED) == true) + val headerAdapter = FollowRequestsHeaderAdapter(accountManager.activeAccount!!.domain, arguments?.getBoolean(ARG_ACCOUNT_LOCKED) == true) val followRequestsAdapter = FollowRequestsAdapter(this, animateAvatar, animateEmojis) binding.recyclerView.adapter = ConcatAdapter(headerAdapter, followRequestsAdapter) followRequestsAdapter 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 0362da9c7..53e174bfc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt @@ -36,6 +36,7 @@ import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.databinding.FragmentViewImageBinding import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.util.hide +import com.keylesspalace.tusky.util.parcelable import com.keylesspalace.tusky.util.visible import io.reactivex.rxjava3.subjects.BehaviorSubject import kotlin.math.abs @@ -92,7 +93,7 @@ class ViewImageFragment : ViewMediaFragment() { super.onViewCreated(view, savedInstanceState) val arguments = this.requireArguments() - val attachment = arguments.getParcelable(ARG_ATTACHMENT) + val attachment: Attachment? = arguments.parcelable(ARG_ATTACHMENT) 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/ViewVideoFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt index 214741a8e..6ae334988 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt @@ -31,6 +31,7 @@ import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.databinding.FragmentViewVideoBinding import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.util.hide +import com.keylesspalace.tusky.util.parcelable import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView @@ -170,7 +171,7 @@ class ViewVideoFragment : ViewMediaFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val attachment = arguments?.getParcelable(ARG_ATTACHMENT) + val attachment: Attachment? = requireArguments().parcelable(ARG_ATTACHMENT) if (attachment == null) { throw IllegalArgumentException("attachment has to be set") 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 c1f5a2cca..6ef027f2a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt +++ b/app/src/main/java/com/keylesspalace/tusky/receiver/SendStatusBroadcastReceiver.kt @@ -29,6 +29,7 @@ import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.service.SendStatusService import com.keylesspalace.tusky.service.StatusToSend import com.keylesspalace.tusky.util.randomAlphanumericString +import com.keylesspalace.tusky.util.requireSerializableExtra import dagger.android.AndroidInjection import javax.inject.Inject @@ -48,7 +49,7 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() { 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: Status.Visibility = intent.requireSerializableExtra(NotificationHelper.KEY_VISIBILITY)!! val spoiler = intent.getStringExtra(NotificationHelper.KEY_SPOILER) ?: "" val mentions = intent.getStringArrayExtra(NotificationHelper.KEY_MENTIONS) ?: emptyArray() 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 3cdd27c33..d47105525 100644 --- a/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt +++ b/app/src/main/java/com/keylesspalace/tusky/service/SendStatusService.kt @@ -31,6 +31,7 @@ import com.keylesspalace.tusky.entity.NewPoll import com.keylesspalace.tusky.entity.NewStatus import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.util.parcelableExtra import dagger.android.AndroidInjection import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -72,7 +73,7 @@ class SendStatusService : Service(), Injectable { override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { if (intent.hasExtra(KEY_STATUS)) { - val statusToSend = intent.getParcelableExtra(KEY_STATUS) + val statusToSend: StatusToSend = intent.parcelableExtra(KEY_STATUS) ?: throw IllegalStateException("SendStatusService started without $KEY_STATUS extra") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app/src/main/java/com/keylesspalace/tusky/util/CompatExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/CompatExtensions.kt new file mode 100644 index 000000000..55d2f44b4 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/CompatExtensions.kt @@ -0,0 +1,49 @@ +@file:Suppress("DEPRECATION") + +package com.keylesspalace.tusky.util + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import java.io.Serializable + +inline fun Intent.requireSerializableExtra(name: String?): T { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getSerializableExtra(name, T::class.java)!! + } else { + getSerializableExtra(name) as T + } +} + +inline fun Intent.parcelableArrayListExtra(name: String?): ArrayList? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelableArrayListExtra(name, T::class.java) + } else { + getParcelableArrayListExtra(name) + } +} + +inline fun Bundle.parcelable(name: String?): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelable(name, T::class.java) + } else { + getParcelable(name) + } +} + +inline fun Intent.parcelableExtra(name: String?): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelableExtra(name, T::class.java) + } else { + getParcelableExtra(name) as T? + } +} + +inline fun Bundle.requireSerializable(name: String?): T { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getSerializable(name, T::class.java)!! + } else { + getSerializable(name) as T + } +} diff --git a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt index 6542b603d..bfc4282be 100644 --- a/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/ComposeActivityTest.kt @@ -464,7 +464,7 @@ class ComposeActivityTest { } private fun clickBack() { - activity.onBackPressed() + activity.onBackPressedDispatcher.onBackPressed() } private fun insertSomeTextInContent(text: String? = null) {