full sdk 34 support (#4224)
builds upon work from #4082 Additionally fixes some deprecations and adds support for [predictive back](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture). I also refactored how the activity transitions work because they are closely related to predictive back. The awkward `finishWithoutSlideOutAnimation` is gone, activities that have been started with slide in will now automatically close with slide out. To test predictive back you need an emulator or device with Sdk 34 (Android 14) and then enable it in the developer settings. Predictive back requires the back action to be determined before it actually occurs so the system can play the right predictive animation, which made a few reorganisations necessary. closes #4082 closes #4005 unlocks a bunch of dependency upgrades that require sdk 34 --------- Co-authored-by: Goooler <wangzongler@gmail.com>
This commit is contained in:
parent
fa8bede7d6
commit
b976fe5296
|
@ -22,13 +22,13 @@ final def CUSTOM_INSTANCE = ""
|
||||||
final def SUPPORT_ACCOUNT_URL = "https://mastodon.social/@Tusky"
|
final def SUPPORT_ACCOUNT_URL = "https://mastodon.social/@Tusky"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 33
|
compileSdk 34
|
||||||
namespace "com.keylesspalace.tusky"
|
namespace "com.keylesspalace.tusky"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId APP_ID
|
applicationId APP_ID
|
||||||
namespace "com.keylesspalace.tusky"
|
namespace "com.keylesspalace.tusky"
|
||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 33
|
targetSdk 34
|
||||||
versionCode 117
|
versionCode 117
|
||||||
versionName "24.1"
|
versionName "24.1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/TuskyTheme"
|
android:theme="@style/TuskyTheme"
|
||||||
android:usesCleartextTraffic="false"
|
android:usesCleartextTraffic="false"
|
||||||
android:localeConfig="@xml/locales_config">
|
android:localeConfig="@xml/locales_config"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".SplashActivity"
|
android:name=".SplashActivity"
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.util.NoUnderlineURLSpan
|
import com.keylesspalace.tusky.util.NoUnderlineURLSpan
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.content.res.Configuration;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -49,6 +50,7 @@ import com.keylesspalace.tusky.interfaces.AccountSelectionListener;
|
||||||
import com.keylesspalace.tusky.interfaces.PermissionRequester;
|
import com.keylesspalace.tusky.interfaces.PermissionRequester;
|
||||||
import com.keylesspalace.tusky.settings.AppTheme;
|
import com.keylesspalace.tusky.settings.AppTheme;
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys;
|
import com.keylesspalace.tusky.settings.PrefKeys;
|
||||||
|
import com.keylesspalace.tusky.util.ActivityExtensions;
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -60,6 +62,9 @@ import javax.inject.Inject;
|
||||||
import static com.keylesspalace.tusky.settings.PrefKeys.APP_THEME;
|
import static com.keylesspalace.tusky.settings.PrefKeys.APP_THEME;
|
||||||
|
|
||||||
public abstract class BaseActivity extends AppCompatActivity implements Injectable {
|
public abstract class BaseActivity extends AppCompatActivity implements Injectable {
|
||||||
|
|
||||||
|
public static final String OPEN_WITH_SLIDE_IN = "OPEN_WITH_SLIDE_IN";
|
||||||
|
|
||||||
private static final String TAG = "BaseActivity";
|
private static final String TAG = "BaseActivity";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -73,6 +78,11 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && getIntent().getBooleanExtra(OPEN_WITH_SLIDE_IN, false)) {
|
||||||
|
overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, R.anim.slide_from_right, R.anim.slide_to_left);
|
||||||
|
overrideActivityTransition(OVERRIDE_TRANSITION_CLOSE, R.anim.slide_from_left, R.anim.slide_to_right);
|
||||||
|
}
|
||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
|
||||||
/* There isn't presently a way to globally change the theme of a whole application at
|
/* There isn't presently a way to globally change the theme of a whole application at
|
||||||
|
@ -166,11 +176,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startActivityWithSlideInAnimation(@NonNull Intent intent) {
|
|
||||||
super.startActivity(intent);
|
|
||||||
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
if (item.getItemId() == android.R.id.home) {
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
@ -183,11 +188,10 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
@Override
|
@Override
|
||||||
public void finish() {
|
public void finish() {
|
||||||
super.finish();
|
super.finish();
|
||||||
overridePendingTransition(R.anim.slide_from_left, R.anim.slide_to_right);
|
// if this activity was opened with slide-in, close it with slide out
|
||||||
}
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE && getIntent().getBooleanExtra(OPEN_WITH_SLIDE_IN, false)) {
|
||||||
|
overridePendingTransition(R.anim.slide_from_left, R.anim.slide_to_right);
|
||||||
public void finishWithoutSlideOutAnimation() {
|
}
|
||||||
super.finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void redirectIfNotLoggedIn() {
|
protected void redirectIfNotLoggedIn() {
|
||||||
|
@ -195,7 +199,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
Intent intent = new Intent(this, LoginActivity.class);
|
Intent intent = new Intent(this, LoginActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
startActivityWithSlideInAnimation(intent);
|
ActivityExtensions.startActivityWithSlideInAnimation(this, intent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,9 +239,9 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
adapter.addAll(accounts);
|
adapter.addAll(accounts);
|
||||||
|
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setTitle(dialogTitle)
|
.setTitle(dialogTitle)
|
||||||
.setAdapter(adapter, (dialogInterface, index) -> listener.onAccountSelected(accounts.get(index)))
|
.setAdapter(adapter, (dialogInterface, index) -> listener.onAccountSelected(accounts.get(index)))
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getOpenAsText() {
|
public @Nullable String getOpenAsText() {
|
||||||
|
@ -263,7 +267,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
Intent intent = MainActivity.redirectIntent(this, account.getId(), url);
|
Intent intent = MainActivity.redirectIntent(this, account.getId(), url);
|
||||||
|
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finishWithoutSlideOutAnimation();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.keylesspalace.tusky.components.viewthread.ViewThreadActivity
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.util.looksLikeMastodonUrl
|
import com.keylesspalace.tusky.util.looksLikeMastodonUrl
|
||||||
import com.keylesspalace.tusky.util.openLink
|
import com.keylesspalace.tusky.util.openLink
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
@ -230,18 +231,33 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val onBackCallback = object : OnBackPressedCallback(enabled = true) {
|
binding.displayNameEditText.doAfterTextChanged {
|
||||||
override fun handleOnBackPressed() = checkForUnsavedChanges()
|
viewModel.dataChanged(currentProfileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.displayNameEditText.doAfterTextChanged {
|
||||||
|
viewModel.dataChanged(currentProfileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.lockedCheckBox.setOnCheckedChangeListener { _, _ ->
|
||||||
|
viewModel.dataChanged(currentProfileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountFieldEditAdapter.onFieldsChanged = {
|
||||||
|
viewModel.dataChanged(currentProfileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
val onBackCallback = object : OnBackPressedCallback(enabled = false) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
showUnsavedChangesDialog()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(this, onBackCallback)
|
onBackPressedDispatcher.addCallback(this, onBackCallback)
|
||||||
}
|
lifecycleScope.launch {
|
||||||
|
viewModel.isChanged.collect { dataWasChanged ->
|
||||||
fun checkForUnsavedChanges() {
|
onBackCallback.isEnabled = dataWasChanged
|
||||||
if (viewModel.hasUnsavedChanges(currentProfileData)) {
|
}
|
||||||
showUnsavedChangesDialog()
|
|
||||||
} else {
|
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import com.keylesspalace.tusky.entity.MastoList
|
||||||
import com.keylesspalace.tusky.util.BindingHolder
|
import com.keylesspalace.tusky.util.BindingHolder
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.util.visible
|
import com.keylesspalace.tusky.util.visible
|
||||||
import com.keylesspalace.tusky.viewmodel.ListsViewModel
|
import com.keylesspalace.tusky.viewmodel.ListsViewModel
|
||||||
|
|
|
@ -51,6 +51,7 @@ import androidx.core.view.GravityCompat
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
import androidx.core.view.forEach
|
import androidx.core.view.forEach
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.viewpager2.widget.MarginPageTransformer
|
import androidx.viewpager2.widget.MarginPageTransformer
|
||||||
|
@ -107,6 +108,7 @@ import com.keylesspalace.tusky.util.getDimension
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.reduceSwipeSensitivity
|
import com.keylesspalace.tusky.util.reduceSwipeSensitivity
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.keylesspalace.tusky.util.updateShortcut
|
import com.keylesspalace.tusky.util.updateShortcut
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
|
@ -185,6 +187,19 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
|
|
||||||
private var directMessageTab: TabLayout.Tab? = null
|
private var directMessageTab: TabLayout.Tab? = null
|
||||||
|
|
||||||
|
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
when {
|
||||||
|
binding.mainDrawerLayout.isOpen -> {
|
||||||
|
binding.mainDrawerLayout.close()
|
||||||
|
}
|
||||||
|
binding.viewPager.currentItem != 0 -> {
|
||||||
|
binding.viewPager.currentItem = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -373,24 +388,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
|
|
||||||
selectedEmojiPack = preferences.getString(EMOJI_PREFERENCE, "")
|
selectedEmojiPack = preferences.getString(EMOJI_PREFERENCE, "")
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(
|
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||||
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 (
|
if (
|
||||||
Build.VERSION.SDK_INT >= 33 &&
|
Build.VERSION.SDK_INT >= 33 &&
|
||||||
|
@ -616,6 +614,19 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
)
|
)
|
||||||
setSavedInstance(savedInstanceState)
|
setSavedInstance(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
binding.mainDrawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
||||||
|
override fun onDrawerSlide(drawerView: View, slideOffset: Float) { }
|
||||||
|
|
||||||
|
override fun onDrawerOpened(drawerView: View) {
|
||||||
|
onBackPressedCallback.isEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerClosed(drawerView: View) {
|
||||||
|
onBackPressedCallback.isEnabled = binding.tabLayout.selectedTabPosition > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDrawerStateChanged(newState: Int) { }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshMainDrawerItems(
|
private fun refreshMainDrawerItems(
|
||||||
|
@ -876,6 +887,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
|
|
||||||
onTabSelectedListener = object : OnTabSelectedListener {
|
onTabSelectedListener = object : OnTabSelectedListener {
|
||||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||||
|
onBackPressedCallback.isEnabled = tab.position > 0 || binding.mainDrawerLayout.isOpen
|
||||||
|
|
||||||
binding.mainToolbar.title = tab.contentDescription
|
binding.mainToolbar.title = tab.contentDescription
|
||||||
|
|
||||||
refreshComposeButtonState(tabAdapter, tab.position)
|
refreshComposeButtonState(tabAdapter, tab.position)
|
||||||
|
@ -964,8 +977,13 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
intent.putExtras(forward)
|
intent.putExtras(forward)
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
overridePendingTransition(R.anim.explode, R.anim.explode)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
overrideActivityTransition(OVERRIDE_TRANSITION_OPEN, R.anim.explode, R.anim.explode)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
overridePendingTransition(R.anim.explode, R.anim.explode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logout() {
|
private fun logout() {
|
||||||
|
@ -988,7 +1006,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
LoginActivity.getIntent(this@MainActivity, LoginActivity.MODE_DEFAULT)
|
LoginActivity.getIntent(this@MainActivity, LoginActivity.MODE_DEFAULT)
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
|
|
@ -35,6 +35,7 @@ import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
|
||||||
import com.keylesspalace.tusky.entity.Filter
|
import com.keylesspalace.tusky.entity.Filter
|
||||||
import com.keylesspalace.tusky.entity.FilterV1
|
import com.keylesspalace.tusky.entity.FilterV1
|
||||||
import com.keylesspalace.tusky.util.isHttpNotFound
|
import com.keylesspalace.tusky.util.isHttpNotFound
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
|
|
|
@ -58,6 +58,7 @@ import com.keylesspalace.tusky.fragment.ViewVideoFragment
|
||||||
import com.keylesspalace.tusky.pager.ImagePagerAdapter
|
import com.keylesspalace.tusky.pager.ImagePagerAdapter
|
||||||
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
|
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
|
||||||
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
|
|
|
@ -24,7 +24,9 @@ import com.keylesspalace.tusky.entity.StringField
|
||||||
import com.keylesspalace.tusky.util.BindingHolder
|
import com.keylesspalace.tusky.util.BindingHolder
|
||||||
import com.keylesspalace.tusky.util.fixTextSelection
|
import com.keylesspalace.tusky.util.fixTextSelection
|
||||||
|
|
||||||
class AccountFieldEditAdapter : RecyclerView.Adapter<BindingHolder<ItemEditFieldBinding>>() {
|
class AccountFieldEditAdapter(
|
||||||
|
var onFieldsChanged: () -> Unit = { }
|
||||||
|
) : RecyclerView.Adapter<BindingHolder<ItemEditFieldBinding>>() {
|
||||||
|
|
||||||
private val fieldData = mutableListOf<MutableStringPair>()
|
private val fieldData = mutableListOf<MutableStringPair>()
|
||||||
private var maxNameLength: Int? = null
|
private var maxNameLength: Int? = null
|
||||||
|
@ -90,10 +92,12 @@ class AccountFieldEditAdapter : RecyclerView.Adapter<BindingHolder<ItemEditField
|
||||||
|
|
||||||
holder.binding.accountFieldNameText.doAfterTextChanged { newText ->
|
holder.binding.accountFieldNameText.doAfterTextChanged { newText ->
|
||||||
fieldData.getOrNull(holder.bindingAdapterPosition)?.first = newText.toString()
|
fieldData.getOrNull(holder.bindingAdapterPosition)?.first = newText.toString()
|
||||||
|
onFieldsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.binding.accountFieldValueText.doAfterTextChanged { newText ->
|
holder.binding.accountFieldValueText.doAfterTextChanged { newText ->
|
||||||
fieldData.getOrNull(holder.bindingAdapterPosition)?.second = newText.toString()
|
fieldData.getOrNull(holder.bindingAdapterPosition)?.second = newText.toString()
|
||||||
|
onFieldsChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the textview contents are selectable
|
// Ensure the textview contents are selectable
|
||||||
|
|
|
@ -92,6 +92,7 @@ import com.keylesspalace.tusky.util.parseAsMastodonHtml
|
||||||
import com.keylesspalace.tusky.util.reduceSwipeSensitivity
|
import com.keylesspalace.tusky.util.reduceSwipeSensitivity
|
||||||
import com.keylesspalace.tusky.util.setClickableText
|
import com.keylesspalace.tusky.util.setClickableText
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.util.visible
|
import com.keylesspalace.tusky.util.visible
|
||||||
|
|
|
@ -31,7 +31,6 @@ import at.connyduck.calladapter.networkresult.fold
|
||||||
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
|
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
|
||||||
import autodispose2.autoDispose
|
import autodispose2.autoDispose
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
|
||||||
import com.keylesspalace.tusky.BottomSheetActivity
|
import com.keylesspalace.tusky.BottomSheetActivity
|
||||||
import com.keylesspalace.tusky.PostLookupFallbackBehavior
|
import com.keylesspalace.tusky.PostLookupFallbackBehavior
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
|
@ -56,6 +55,7 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener
|
import com.keylesspalace.tusky.view.EndlessOnScrollListener
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
@ -144,17 +144,13 @@ class AccountListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewTag(tag: String) {
|
override fun onViewTag(tag: String) {
|
||||||
(activity as BaseActivity?)
|
activity?.startActivityWithSlideInAnimation(
|
||||||
?.startActivityWithSlideInAnimation(
|
StatusListActivity.newHashtagIntent(requireContext(), tag)
|
||||||
StatusListActivity.newHashtagIntent(requireContext(), tag)
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewAccount(id: String) {
|
override fun onViewAccount(id: String) {
|
||||||
(activity as BaseActivity?)?.let {
|
activity?.startActivityWithSlideInAnimation(AccountActivity.getIntent(requireContext(), id))
|
||||||
val intent = AccountActivity.getIntent(it, id)
|
|
||||||
it.startActivityWithSlideInAnimation(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewUrl(url: String) {
|
override fun onViewUrl(url: String) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import com.keylesspalace.tusky.util.Loading
|
||||||
import com.keylesspalace.tusky.util.Success
|
import com.keylesspalace.tusky.util.Success
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.view.EmojiPicker
|
import com.keylesspalace.tusky.view.EmojiPicker
|
||||||
|
|
|
@ -70,6 +70,7 @@ import com.canhub.cropper.CropImage
|
||||||
import com.canhub.cropper.CropImageContract
|
import com.canhub.cropper.CropImageContract
|
||||||
import com.canhub.cropper.options
|
import com.canhub.cropper.options
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
import com.keylesspalace.tusky.BaseActivity
|
||||||
|
@ -215,6 +216,24 @@ class ComposeActivity :
|
||||||
viewModel.cropImageItemOld = null
|
viewModel.cropImageItemOld = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val onBackPressedCallback = object : OnBackPressedCallback(false) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -425,7 +444,10 @@ class ComposeActivity :
|
||||||
if (startingContentWarning != null) {
|
if (startingContentWarning != null) {
|
||||||
binding.composeContentWarningField.setText(startingContentWarning)
|
binding.composeContentWarningField.setText(startingContentWarning)
|
||||||
}
|
}
|
||||||
binding.composeContentWarningField.doOnTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
|
binding.composeContentWarningField.doOnTextChanged { newContentWarning, _, _, _ ->
|
||||||
|
updateVisibleCharactersLeft()
|
||||||
|
viewModel.updateContentWarning(newContentWarning?.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupComposeField(preferences: SharedPreferences, startingText: String?) {
|
private fun setupComposeField(preferences: SharedPreferences, startingText: String?) {
|
||||||
|
@ -456,6 +478,7 @@ class ComposeActivity :
|
||||||
binding.composeEditField.doAfterTextChanged { editable ->
|
binding.composeEditField.doAfterTextChanged { editable ->
|
||||||
highlightSpans(editable!!, mentionColour)
|
highlightSpans(editable!!, mentionColour)
|
||||||
updateVisibleCharactersLeft()
|
updateVisibleCharactersLeft()
|
||||||
|
viewModel.updateContent(editable.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
|
// work around Android platform bug -> https://issuetracker.google.com/issues/67102093
|
||||||
|
@ -547,6 +570,12 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.closeConfirmation.collect {
|
||||||
|
updateOnBackPressedCallbackState()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupButtons() {
|
private fun setupButtons() {
|
||||||
|
@ -557,6 +586,17 @@ class ComposeActivity :
|
||||||
scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView)
|
scheduleBehavior = BottomSheetBehavior.from(binding.composeScheduleView)
|
||||||
emojiBehavior = BottomSheetBehavior.from(binding.emojiView)
|
emojiBehavior = BottomSheetBehavior.from(binding.emojiView)
|
||||||
|
|
||||||
|
val bottomSheetCallback = object : BottomSheetCallback() {
|
||||||
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
|
updateOnBackPressedCallbackState()
|
||||||
|
}
|
||||||
|
override fun onSlide(bottomSheet: View, slideOffset: Float) { }
|
||||||
|
}
|
||||||
|
composeOptionsBehavior.addBottomSheetCallback(bottomSheetCallback)
|
||||||
|
addMediaBehavior.addBottomSheetCallback(bottomSheetCallback)
|
||||||
|
scheduleBehavior.addBottomSheetCallback(bottomSheetCallback)
|
||||||
|
emojiBehavior.addBottomSheetCallback(bottomSheetCallback)
|
||||||
|
|
||||||
enableButton(binding.composeEmojiButton, clickable = false, colorActive = false)
|
enableButton(binding.composeEmojiButton, clickable = false, colorActive = false)
|
||||||
|
|
||||||
// Setup the interface buttons.
|
// Setup the interface buttons.
|
||||||
|
@ -618,26 +658,7 @@ class ComposeActivity :
|
||||||
binding.actionPhotoPick.setOnClickListener { onMediaPick() }
|
binding.actionPhotoPick.setOnClickListener { onMediaPick() }
|
||||||
binding.addPollTextActionTextView.setOnClickListener { openPollDialog() }
|
binding.addPollTextActionTextView.setOnClickListener { openPollDialog() }
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(
|
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||||
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(initialLanguages: List<String>) {
|
private fun setupLanguageSpinner(initialLanguages: List<String>) {
|
||||||
|
@ -690,6 +711,15 @@ class ComposeActivity :
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateOnBackPressedCallbackState() {
|
||||||
|
val confirmation = viewModel.closeConfirmation.value
|
||||||
|
onBackPressedCallback.isEnabled = confirmation != ConfirmationKind.NONE ||
|
||||||
|
composeOptionsBehavior.state != BottomSheetBehavior.STATE_HIDDEN ||
|
||||||
|
addMediaBehavior.state != BottomSheetBehavior.STATE_HIDDEN ||
|
||||||
|
emojiBehavior.state != BottomSheetBehavior.STATE_HIDDEN ||
|
||||||
|
scheduleBehavior.state != BottomSheetBehavior.STATE_HIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
private fun replaceTextAtCaret(text: CharSequence) {
|
private fun replaceTextAtCaret(text: CharSequence) {
|
||||||
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd
|
// If you select "backward" in an editable, you get SelectionStart > SelectionEnd
|
||||||
val start = binding.composeEditField.selectionStart.coerceAtMost(
|
val start = binding.composeEditField.selectionStart.coerceAtMost(
|
||||||
|
@ -1004,7 +1034,7 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removePoll() {
|
private fun removePoll() {
|
||||||
viewModel.poll.value = null
|
viewModel.updatePoll(null)
|
||||||
binding.pollPreview.hide()
|
binding.pollPreview.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1219,7 +1249,7 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pickMedia(uri: Uri, description: String? = null) {
|
private fun pickMedia(uri: Uri, description: String? = null) {
|
||||||
var sanitizedDescription = sanitizePickMediaDescription(description)
|
val sanitizedDescription = sanitizePickMediaDescription(description)
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.pickMedia(uri, sanitizedDescription).onFailure { throwable ->
|
viewModel.pickMedia(uri, sanitizedDescription).onFailure { throwable ->
|
||||||
|
@ -1292,10 +1322,10 @@ class ComposeActivity :
|
||||||
private fun handleCloseButton() {
|
private fun handleCloseButton() {
|
||||||
val contentText = binding.composeEditField.text.toString()
|
val contentText = binding.composeEditField.text.toString()
|
||||||
val contentWarning = binding.composeContentWarningField.text.toString()
|
val contentWarning = binding.composeContentWarningField.text.toString()
|
||||||
when (viewModel.handleCloseButton(contentText, contentWarning)) {
|
when (viewModel.closeConfirmation.value) {
|
||||||
ConfirmationKind.NONE -> {
|
ConfirmationKind.NONE -> {
|
||||||
viewModel.stopUploads()
|
viewModel.stopUploads()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
ConfirmationKind.SAVE_OR_DISCARD ->
|
ConfirmationKind.SAVE_OR_DISCARD ->
|
||||||
getSaveAsDraftOrDiscardDialog(contentText, contentWarning).show()
|
getSaveAsDraftOrDiscardDialog(contentText, contentWarning).show()
|
||||||
|
@ -1355,7 +1385,7 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.action_discard) { _, _ ->
|
.setNegativeButton(R.string.action_discard) { _, _ ->
|
||||||
viewModel.stopUploads()
|
viewModel.stopUploads()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1371,7 +1401,7 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.action_discard) { _, _ ->
|
.setNegativeButton(R.string.action_discard) { _, _ ->
|
||||||
viewModel.stopUploads()
|
viewModel.stopUploads()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,7 +1415,7 @@ class ComposeActivity :
|
||||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
.setPositiveButton(R.string.action_delete) { _, _ ->
|
||||||
viewModel.deleteDraft()
|
viewModel.deleteDraft()
|
||||||
viewModel.stopUploads()
|
viewModel.stopUploads()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.action_continue_edit) { _, _ ->
|
.setNegativeButton(R.string.action_continue_edit) { _, _ ->
|
||||||
// Do nothing, dialog will dismiss, user can continue editing
|
// Do nothing, dialog will dismiss, user can continue editing
|
||||||
|
@ -1394,7 +1424,7 @@ class ComposeActivity :
|
||||||
|
|
||||||
private fun deleteDraftAndFinish() {
|
private fun deleteDraftAndFinish() {
|
||||||
viewModel.deleteDraft()
|
viewModel.deleteDraft()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDraftAndFinish(contentText: String, contentWarning: String) {
|
private fun saveDraftAndFinish(contentText: String, contentWarning: String) {
|
||||||
|
@ -1412,7 +1442,7 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
viewModel.saveDraft(contentText, contentWarning)
|
viewModel.saveDraft(contentText, contentWarning)
|
||||||
dialog?.cancel()
|
dialog?.cancel()
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,9 @@ class ComposeViewModel @Inject constructor(
|
||||||
private var modifiedInitialState: Boolean = false
|
private var modifiedInitialState: Boolean = false
|
||||||
private var hasScheduledTimeChanged: Boolean = false
|
private var hasScheduledTimeChanged: Boolean = false
|
||||||
|
|
||||||
|
private var currentContent: String? = ""
|
||||||
|
private var currentContentWarning: String? = ""
|
||||||
|
|
||||||
val instanceInfo: SharedFlow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
|
val instanceInfo: SharedFlow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
|
||||||
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
|
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
@ -99,6 +102,8 @@ class ComposeViewModel @Inject constructor(
|
||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val closeConfirmation = MutableStateFlow(ConfirmationKind.NONE)
|
||||||
|
|
||||||
private lateinit var composeKind: ComposeKind
|
private lateinit var composeKind: ComposeKind
|
||||||
|
|
||||||
// Used in ComposeActivity to pass state to result function when cropImage contract inflight
|
// Used in ComposeActivity to pass state to result function when cropImage contract inflight
|
||||||
|
@ -199,6 +204,7 @@ class ComposeViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateCloseConfirmation()
|
||||||
return mediaItem
|
return mediaItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,21 +234,37 @@ class ComposeViewModel @Inject constructor(
|
||||||
fun removeMediaFromQueue(item: QueuedMedia) {
|
fun removeMediaFromQueue(item: QueuedMedia) {
|
||||||
mediaUploader.cancelUploadScope(item.localId)
|
mediaUploader.cancelUploadScope(item.localId)
|
||||||
media.update { mediaList -> mediaList.filter { it.localId != item.localId } }
|
media.update { mediaList -> mediaList.filter { it.localId != item.localId } }
|
||||||
|
updateCloseConfirmation()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleMarkSensitive() {
|
fun toggleMarkSensitive() {
|
||||||
this.markMediaAsSensitive.value = this.markMediaAsSensitive.value != true
|
this.markMediaAsSensitive.value = this.markMediaAsSensitive.value != true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleCloseButton(contentText: String?, contentWarning: String?): ConfirmationKind {
|
fun updateContent(newContent: String?) {
|
||||||
return if (didChange(contentText, contentWarning)) {
|
currentContent = newContent
|
||||||
|
updateCloseConfirmation()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateContentWarning(newContentWarning: String?) {
|
||||||
|
currentContentWarning = newContentWarning
|
||||||
|
updateCloseConfirmation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCloseConfirmation() {
|
||||||
|
val contentWarning = if (showContentWarning.value) {
|
||||||
|
currentContentWarning
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
this.closeConfirmation.value = if (didChange(currentContent, contentWarning)) {
|
||||||
when (composeKind) {
|
when (composeKind) {
|
||||||
ComposeKind.NEW -> if (isEmpty(contentText, contentWarning)) {
|
ComposeKind.NEW -> if (isEmpty(currentContent, contentWarning)) {
|
||||||
ConfirmationKind.NONE
|
ConfirmationKind.NONE
|
||||||
} else {
|
} else {
|
||||||
ConfirmationKind.SAVE_OR_DISCARD
|
ConfirmationKind.SAVE_OR_DISCARD
|
||||||
}
|
}
|
||||||
ComposeKind.EDIT_DRAFT -> if (isEmpty(contentText, contentWarning)) {
|
ComposeKind.EDIT_DRAFT -> if (isEmpty(currentContent, contentWarning)) {
|
||||||
ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_DRAFT
|
ConfirmationKind.CONTINUE_EDITING_OR_DISCARD_DRAFT
|
||||||
} else {
|
} else {
|
||||||
ConfirmationKind.UPDATE_OR_DISCARD
|
ConfirmationKind.UPDATE_OR_DISCARD
|
||||||
|
@ -272,6 +294,7 @@ class ComposeViewModel @Inject constructor(
|
||||||
fun contentWarningChanged(value: Boolean) {
|
fun contentWarningChanged(value: Boolean) {
|
||||||
showContentWarning.value = value
|
showContentWarning.value = value
|
||||||
contentWarningStateChanged = true
|
contentWarningStateChanged = true
|
||||||
|
updateCloseConfirmation()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteDraft() {
|
fun deleteDraft() {
|
||||||
|
@ -511,11 +534,14 @@ class ComposeViewModel @Inject constructor(
|
||||||
replyingStatusContent = composeOptions?.replyingStatusContent
|
replyingStatusContent = composeOptions?.replyingStatusContent
|
||||||
replyingStatusAuthor = composeOptions?.replyingStatusAuthor
|
replyingStatusAuthor = composeOptions?.replyingStatusAuthor
|
||||||
|
|
||||||
|
updateCloseConfirmation()
|
||||||
|
|
||||||
setupComplete = true
|
setupComplete = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePoll(newPoll: NewPoll) {
|
fun updatePoll(newPoll: NewPoll?) {
|
||||||
poll.value = newPoll
|
poll.value = newPoll
|
||||||
|
updateCloseConfirmation()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateScheduledAt(newScheduledAt: String?) {
|
fun updateScheduledAt(newScheduledAt: String?) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.entity.Filter
|
import com.keylesspalace.tusky.entity.Filter
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.util.visible
|
import com.keylesspalace.tusky.util.visible
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -110,8 +111,7 @@ class FiltersActivity : BaseActivity(), FiltersListener {
|
||||||
putExtra(EditFilterActivity.FILTER_TO_EDIT, filter)
|
putExtra(EditFilterActivity.FILTER_TO_EDIT, filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivityWithSlideInAnimation(intent)
|
||||||
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteFilter(filter: Filter) {
|
override fun deleteFilter(filter: Filter) {
|
||||||
|
|
|
@ -227,14 +227,10 @@ class LoginWebViewActivity : BaseActivity(), Injectable {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finish() {
|
|
||||||
super.finishWithoutSlideOutAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun requiresLogin() = false
|
override fun requiresLogin() = false
|
||||||
|
|
||||||
private fun sendResult(result: LoginResult) {
|
private fun sendResult(result: LoginResult) {
|
||||||
setResult(Activity.RESULT_OK, OauthLogin.makeResultIntent(result))
|
setResult(Activity.RESULT_OK, OauthLogin.makeResultIntent(result))
|
||||||
finishWithoutSlideOutAnimation()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ import com.keylesspalace.tusky.util.getInitialLanguages
|
||||||
import com.keylesspalace.tusky.util.getLocaleList
|
import com.keylesspalace.tusky.util.getLocaleList
|
||||||
import com.keylesspalace.tusky.util.getTuskyDisplayName
|
import com.keylesspalace.tusky.util.getTuskyDisplayName
|
||||||
import com.keylesspalace.tusky.util.makeIcon
|
import com.keylesspalace.tusky.util.makeIcon
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||||
|
@ -100,11 +101,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setIcon(R.drawable.ic_tabs)
|
setIcon(R.drawable.ic_tabs)
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = Intent(context, TabPreferenceActivity::class.java)
|
val intent = Intent(context, TabPreferenceActivity::class.java)
|
||||||
activity?.startActivity(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(
|
|
||||||
R.anim.slide_from_right,
|
|
||||||
R.anim.slide_to_left
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,11 +111,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setIcon(R.drawable.ic_hashtag)
|
setIcon(R.drawable.ic_hashtag)
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = Intent(context, FollowedTagsActivity::class.java)
|
val intent = Intent(context, FollowedTagsActivity::class.java)
|
||||||
activity?.startActivity(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(
|
|
||||||
R.anim.slide_from_right,
|
|
||||||
R.anim.slide_to_left
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,11 +122,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = Intent(context, AccountListActivity::class.java)
|
val intent = Intent(context, AccountListActivity::class.java)
|
||||||
intent.putExtra("type", AccountListActivity.Type.MUTES)
|
intent.putExtra("type", AccountListActivity.Type.MUTES)
|
||||||
activity?.startActivity(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(
|
|
||||||
R.anim.slide_from_right,
|
|
||||||
R.anim.slide_to_left
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,11 +136,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = Intent(context, AccountListActivity::class.java)
|
val intent = Intent(context, AccountListActivity::class.java)
|
||||||
intent.putExtra("type", AccountListActivity.Type.BLOCKS)
|
intent.putExtra("type", AccountListActivity.Type.BLOCKS)
|
||||||
activity?.startActivity(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(
|
|
||||||
R.anim.slide_from_right,
|
|
||||||
R.anim.slide_to_left
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,11 +146,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setIcon(R.drawable.ic_mute_24dp)
|
setIcon(R.drawable.ic_mute_24dp)
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = Intent(context, DomainBlocksActivity::class.java)
|
val intent = Intent(context, DomainBlocksActivity::class.java)
|
||||||
activity?.startActivity(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(
|
|
||||||
R.anim.slide_from_right,
|
|
||||||
R.anim.slide_to_left
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +157,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
setIcon(R.drawable.ic_logout)
|
setIcon(R.drawable.ic_logout)
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val intent = LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION)
|
val intent = LoginActivity.getIntent(context, LoginActivity.MODE_MIGRATION)
|
||||||
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,8 +281,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
it,
|
it,
|
||||||
PreferencesActivity.NOTIFICATION_PREFERENCES
|
PreferencesActivity.NOTIFICATION_PREFERENCES
|
||||||
)
|
)
|
||||||
it.startActivity(intent)
|
it.startActivityWithSlideInAnimation(intent)
|
||||||
it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,8 +348,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
|
|
||||||
private fun launchFilterActivity() {
|
private fun launchFilterActivity() {
|
||||||
val intent = Intent(context, FiltersActivity::class.java)
|
val intent = Intent(context, FiltersActivity::class.java)
|
||||||
activity?.startActivity(intent)
|
(activity as? BaseActivity)?.startActivityWithSlideInAnimation(intent)
|
||||||
activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys.APP_THEME
|
import com.keylesspalace.tusky.settings.PrefKeys.APP_THEME
|
||||||
import com.keylesspalace.tusky.util.getNonNullString
|
import com.keylesspalace.tusky.util.getNonNullString
|
||||||
import com.keylesspalace.tusky.util.setAppNightMode
|
import com.keylesspalace.tusky.util.setAppNightMode
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import dagger.android.DispatchingAndroidInjector
|
import dagger.android.DispatchingAndroidInjector
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -139,16 +140,14 @@ class PreferencesActivity :
|
||||||
).unregisterOnSharedPreferenceChangeListener(this)
|
).unregisterOnSharedPreferenceChangeListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveInstanceState(outState: Bundle) {
|
|
||||||
outState.putBoolean(EXTRA_RESTART_ON_BACK, restartActivitiesOnBackPressedCallback.isEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
outState.putBoolean(EXTRA_RESTART_ON_BACK, restartActivitiesOnBackPressedCallback.isEnabled)
|
outState.putBoolean(EXTRA_RESTART_ON_BACK, restartActivitiesOnBackPressedCallback.isEnabled)
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
|
sharedPreferences ?: return
|
||||||
|
key ?: return
|
||||||
when (key) {
|
when (key) {
|
||||||
APP_THEME -> {
|
APP_THEME -> {
|
||||||
val theme = sharedPreferences.getNonNullString(APP_THEME, AppTheme.DEFAULT.value)
|
val theme = sharedPreferences.getNonNullString(APP_THEME, AppTheme.DEFAULT.value)
|
||||||
|
@ -156,11 +155,11 @@ class PreferencesActivity :
|
||||||
setAppNightMode(theme)
|
setAppNightMode(theme)
|
||||||
|
|
||||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||||
this.restartCurrentActivity()
|
this.recreate()
|
||||||
}
|
}
|
||||||
PrefKeys.UI_TEXT_SCALE_RATIO -> {
|
PrefKeys.UI_TEXT_SCALE_RATIO -> {
|
||||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||||
this.restartCurrentActivity()
|
this.recreate()
|
||||||
}
|
}
|
||||||
PrefKeys.STATUS_TEXT_SIZE, PrefKeys.ABSOLUTE_TIME_VIEW, PrefKeys.SHOW_BOT_OVERLAY, PrefKeys.ANIMATE_GIF_AVATARS, PrefKeys.USE_BLURHASH,
|
PrefKeys.STATUS_TEXT_SIZE, PrefKeys.ABSOLUTE_TIME_VIEW, PrefKeys.SHOW_BOT_OVERLAY, PrefKeys.ANIMATE_GIF_AVATARS, PrefKeys.USE_BLURHASH,
|
||||||
PrefKeys.SHOW_SELF_USERNAME, PrefKeys.SHOW_CARDS_IN_TIMELINES, PrefKeys.CONFIRM_REBLOGS, PrefKeys.CONFIRM_FAVOURITES,
|
PrefKeys.SHOW_SELF_USERNAME, PrefKeys.SHOW_CARDS_IN_TIMELINES, PrefKeys.CONFIRM_REBLOGS, PrefKeys.CONFIRM_FAVOURITES,
|
||||||
|
@ -173,16 +172,6 @@ class PreferencesActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restartCurrentActivity() {
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
val savedInstanceState = Bundle()
|
|
||||||
saveInstanceState(savedInstanceState)
|
|
||||||
intent.putExtras(savedInstanceState)
|
|
||||||
startActivityWithSlideInAnimation(intent)
|
|
||||||
finish()
|
|
||||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun androidInjector() = androidInjector
|
override fun androidInjector() = androidInjector
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -97,10 +97,6 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector, MenuProvider,
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finish() {
|
|
||||||
super.finishWithoutSlideOutAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPageTitle(position: Int): CharSequence {
|
private fun getPageTitle(position: Int): CharSequence {
|
||||||
return when (position) {
|
return when (position) {
|
||||||
0 -> getString(R.string.title_posts)
|
0 -> getString(R.string.title_posts)
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.util.visible
|
import com.keylesspalace.tusky.util.visible
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
|
|
@ -58,6 +58,7 @@ import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.util.CardViewMode
|
import com.keylesspalace.tusky.util.CardViewMode
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||||
import com.keylesspalace.tusky.util.openLink
|
import com.keylesspalace.tusky.util.openLink
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.view.showMuteAccountDialog
|
import com.keylesspalace.tusky.view.showMuteAccountDialog
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
|
|
|
@ -39,7 +39,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||||
import at.connyduck.sparkbutton.helpers.Utils
|
import at.connyduck.sparkbutton.helpers.Utils
|
||||||
import autodispose2.androidx.lifecycle.autoDispose
|
import autodispose2.androidx.lifecycle.autoDispose
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
||||||
import com.keylesspalace.tusky.appstore.EventHub
|
import com.keylesspalace.tusky.appstore.EventHub
|
||||||
|
@ -66,6 +65,7 @@ import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate
|
||||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
|
@ -463,13 +463,13 @@ class TimelineFragment :
|
||||||
override fun onShowReblogs(position: Int) {
|
override fun onShowReblogs(position: Int) {
|
||||||
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
||||||
val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
|
val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
|
||||||
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowFavs(position: Int) {
|
override fun onShowFavs(position: Int) {
|
||||||
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
||||||
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
||||||
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
|
activity?.startActivityWithSlideInAnimation(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoadMore(position: Int) {
|
override fun onLoadMore(position: Int) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||||
import at.connyduck.sparkbutton.helpers.Utils
|
import at.connyduck.sparkbutton.helpers.Utils
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.StatusListActivity
|
import com.keylesspalace.tusky.StatusListActivity
|
||||||
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
|
import com.keylesspalace.tusky.components.trending.viewmodel.TrendingTagsViewModel
|
||||||
|
@ -42,6 +41,7 @@ import com.keylesspalace.tusky.interfaces.RefreshableFragment
|
||||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.viewdata.TrendingViewData
|
import com.keylesspalace.tusky.viewdata.TrendingViewData
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -136,7 +136,7 @@ class TrendingTagsFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onViewTag(tag: String) {
|
fun onViewTag(tag: String) {
|
||||||
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(
|
requireActivity().startActivityWithSlideInAnimation(
|
||||||
StatusListActivity.newHashtagIntent(requireContext(), tag)
|
StatusListActivity.newHashtagIntent(requireContext(), tag)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
|
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
|
||||||
import com.keylesspalace.tusky.components.accountlist.AccountListActivity.Companion.newIntent
|
import com.keylesspalace.tusky.components.accountlist.AccountListActivity.Companion.newIntent
|
||||||
|
@ -53,6 +52,7 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.openLink
|
import com.keylesspalace.tusky.util.openLink
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData.Companion.list
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData.Companion.list
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
|
@ -386,13 +386,13 @@ class ViewThreadFragment :
|
||||||
override fun onShowReblogs(position: Int) {
|
override fun onShowReblogs(position: Int) {
|
||||||
val statusId = adapter.currentList[position].id
|
val statusId = adapter.currentList[position].id
|
||||||
val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
|
val intent = newIntent(requireContext(), AccountListActivity.Type.REBLOGGED, statusId)
|
||||||
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(intent)
|
requireActivity().startActivityWithSlideInAnimation(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onShowFavs(position: Int) {
|
override fun onShowFavs(position: Int) {
|
||||||
val statusId = adapter.currentList[position].id
|
val statusId = adapter.currentList[position].id
|
||||||
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
||||||
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(intent)
|
requireActivity().startActivityWithSlideInAnimation(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
|
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ import com.keylesspalace.tusky.util.emojify
|
||||||
import com.keylesspalace.tusky.util.hide
|
import com.keylesspalace.tusky.util.hide
|
||||||
import com.keylesspalace.tusky.util.loadAvatar
|
import com.keylesspalace.tusky.util.loadAvatar
|
||||||
import com.keylesspalace.tusky.util.show
|
import com.keylesspalace.tusky.util.show
|
||||||
|
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||||
import com.keylesspalace.tusky.util.unicodeWrap
|
import com.keylesspalace.tusky.util.unicodeWrap
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
|
|
@ -171,7 +171,7 @@ class ViewVideoFragment : ViewMediaFragment(), Injectable {
|
||||||
|
|
||||||
/** A fling up/down should dismiss the fragment */
|
/** A fling up/down should dismiss the fragment */
|
||||||
override fun onFling(
|
override fun onFling(
|
||||||
e1: MotionEvent,
|
e1: MotionEvent?,
|
||||||
e2: MotionEvent,
|
e2: MotionEvent,
|
||||||
velocityX: Float,
|
velocityX: Float,
|
||||||
velocityY: Float
|
velocityY: Float
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.service
|
package com.keylesspalace.tusky.service
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.service.quicksettings.TileService
|
import android.service.quicksettings.TileService
|
||||||
import com.keylesspalace.tusky.MainActivity
|
import com.keylesspalace.tusky.MainActivity
|
||||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||||
|
@ -29,6 +31,13 @@ class TuskyTileService : TileService() {
|
||||||
override fun onClick() {
|
override fun onClick() {
|
||||||
val intent = MainActivity.composeIntent(this, ComposeActivity.ComposeOptions())
|
val intent = MainActivity.composeIntent(this, ComposeActivity.ComposeOptions())
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
startActivityAndCollapse(intent)
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
val pendingIntent = PendingIntent.getActivity(this, 1, intent, PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
startActivityAndCollapse(pendingIntent)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityAndCollapse(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
@file:JvmName("ActivityExtensions")
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import com.keylesspalace.tusky.BaseActivity
|
||||||
|
import com.keylesspalace.tusky.R
|
||||||
|
|
||||||
|
fun Activity.startActivityWithSlideInAnimation(intent: Intent) {
|
||||||
|
// the new transition api needs to be called by the activity that is the result of the transition,
|
||||||
|
// so we pass a flag that BaseActivity will respect.
|
||||||
|
intent.putExtra(BaseActivity.OPEN_WITH_SLIDE_IN, true)
|
||||||
|
startActivity(intent)
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
// the old api needs to be called by the activity that starts the transition
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
|
||||||
|
}
|
||||||
|
}
|
|
@ -373,21 +373,21 @@ class ClickableSpanTextView @JvmOverloads constructor(
|
||||||
return firstDiff < secondDiff
|
return firstDiff < secondDiff
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas?) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
// Paint span boundaries. Optimised out on release builds, or debug builds where
|
// Paint span boundaries. Optimised out on release builds, or debug builds where
|
||||||
// showSpanBoundaries is false.
|
// showSpanBoundaries is false.
|
||||||
if (BuildConfig.DEBUG && showSpanBoundaries) {
|
if (BuildConfig.DEBUG && showSpanBoundaries) {
|
||||||
canvas?.save()
|
canvas.save()
|
||||||
for (entry in delegateRects) {
|
for (entry in delegateRects) {
|
||||||
canvas?.drawRect(entry.key, paddingDebugPaint)
|
canvas.drawRect(entry.key, paddingDebugPaint)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (entry in spanRects) {
|
for (entry in spanRects) {
|
||||||
canvas?.drawRect(entry.key, spanDebugPaint)
|
canvas.drawRect(entry.key, spanDebugPaint)
|
||||||
}
|
}
|
||||||
canvas?.restore()
|
canvas.restore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,14 +265,14 @@ class GraphView @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun dataSpacing(data: List<Any>) = width.toFloat() / max(data.size - 1, 1).toFloat()
|
private fun dataSpacing(data: List<Any>) = width.toFloat() / max(data.size - 1, 1).toFloat()
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas?) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
if (primaryLinePath.isEmpty && width > 0) {
|
if (primaryLinePath.isEmpty && width > 0) {
|
||||||
initializeVertices()
|
initializeVertices()
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas?.apply {
|
canvas.apply {
|
||||||
drawRect(sizeRect, graphPaint)
|
drawRect(sizeRect, graphPaint)
|
||||||
|
|
||||||
val pointDistance = dataSpacing(primaryLineData)
|
val pointDistance = dataSpacing(primaryLineData)
|
||||||
|
|
|
@ -38,6 +38,7 @@ import com.keylesspalace.tusky.util.randomAlphanumericString
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
|
@ -72,6 +73,8 @@ class EditProfileViewModel @Inject constructor(
|
||||||
val instanceData: Flow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
|
val instanceData: Flow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
|
||||||
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
|
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)
|
||||||
|
|
||||||
|
val isChanged = MutableStateFlow(false)
|
||||||
|
|
||||||
private var apiProfileAccount: Account? = null
|
private var apiProfileAccount: Account? = null
|
||||||
|
|
||||||
fun obtainProfile() = viewModelScope.launch {
|
fun obtainProfile() = viewModelScope.launch {
|
||||||
|
@ -102,6 +105,10 @@ class EditProfileViewModel @Inject constructor(
|
||||||
headerData.value = getHeaderUri()
|
headerData.value = getHeaderUri()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun dataChanged(newProfileData: ProfileDataInUi) {
|
||||||
|
isChanged.value = getProfileDiff(apiProfileAccount, newProfileData).hasChanges()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun save(newProfileData: ProfileDataInUi) {
|
internal fun save(newProfileData: ProfileDataInUi) {
|
||||||
if (saveData.value is Loading || profileData.value !is Success) {
|
if (saveData.value is Loading || profileData.value !is Success) {
|
||||||
return
|
return
|
||||||
|
@ -178,12 +185,6 @@ class EditProfileViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun hasUnsavedChanges(newProfileData: ProfileDataInUi): Boolean {
|
|
||||||
val diff = getProfileDiff(apiProfileAccount, newProfileData)
|
|
||||||
|
|
||||||
return diff.hasChanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getProfileDiff(
|
private fun getProfileDiff(
|
||||||
oldProfileAccount: Account?,
|
oldProfileAccount: Account?,
|
||||||
newProfileData: ProfileDataInUi
|
newProfileData: ProfileDataInUi
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:interpolator="@android:anim/linear_interpolator"
|
|
||||||
android:fromAlpha="0"
|
|
||||||
android:toAlpha="1"
|
|
||||||
android:duration="300" />
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:interpolator="@android:anim/linear_interpolator"
|
|
||||||
android:fromAlpha="1"
|
|
||||||
android:toAlpha="0"
|
|
||||||
android:duration="300" />
|
|
|
@ -23,8 +23,10 @@ import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.ParameterizedRobolectricTestRunner
|
import org.robolectric.ParameterizedRobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
@RunWith(ParameterizedRobolectricTestRunner::class)
|
@RunWith(ParameterizedRobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [33])
|
||||||
class StatusLengthTest(
|
class StatusLengthTest(
|
||||||
private val text: String,
|
private val text: String,
|
||||||
private val expectedLength: Int
|
private val expectedLength: Int
|
||||||
|
|
Loading…
Reference in New Issue