Add shared element transition for camera fab.
This commit is contained in:
parent
33b88796e8
commit
7f2f5a182f
10 changed files with 174 additions and 19 deletions
|
@ -0,0 +1,53 @@
|
|||
package org.thoughtcrime.securesms.animation.transitions
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.transition.Transition
|
||||
import android.transition.TransitionValues
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
|
||||
@RequiresApi(21)
|
||||
class FabElevationFadeTransform(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
|
||||
|
||||
companion object {
|
||||
private const val ELEVATION = "CrossfaderTransition.ELEVATION"
|
||||
}
|
||||
|
||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||
if (transitionValues.view is FloatingActionButton) {
|
||||
transitionValues.values[ELEVATION] = transitionValues.view.elevation
|
||||
}
|
||||
}
|
||||
|
||||
override fun captureEndValues(transitionValues: TransitionValues) {
|
||||
if (transitionValues.view is FloatingActionButton) {
|
||||
transitionValues.values[ELEVATION] = transitionValues.view.elevation
|
||||
}
|
||||
}
|
||||
|
||||
override fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
|
||||
if (startValues?.view !is FloatingActionButton || endValues?.view !is FloatingActionButton) {
|
||||
return null
|
||||
}
|
||||
|
||||
val startElevation = startValues.view.elevation
|
||||
val endElevation = endValues.view.elevation
|
||||
if (startElevation == endElevation) {
|
||||
return null
|
||||
}
|
||||
|
||||
return ValueAnimator.ofFloat(
|
||||
startValues.values[ELEVATION] as Float,
|
||||
endValues.values[ELEVATION] as Float
|
||||
).apply {
|
||||
addUpdateListener {
|
||||
val elevation = it.animatedValue as Float
|
||||
endValues.view.elevation = elevation
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.R;
|
|||
import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.task.SnackbarAsyncTask;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
|
||||
|
@ -70,10 +71,16 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
|
|||
|
||||
coordinator = view.findViewById(R.id.coordinator);
|
||||
list = view.findViewById(R.id.list);
|
||||
fab = view.findViewById(R.id.fab);
|
||||
cameraFab = view.findViewById(R.id.camera_fab);
|
||||
emptyState = new Stub<>(view.findViewById(R.id.empty_state));
|
||||
|
||||
if (FeatureFlags.internalUser()) {
|
||||
fab = view.findViewById(R.id.fab_new);
|
||||
cameraFab = view.findViewById(R.id.camera_fab_new);
|
||||
} else {
|
||||
fab = view.findViewById(R.id.fab_old);
|
||||
cameraFab = view.findViewById(R.id.camera_fab_old);
|
||||
}
|
||||
|
||||
toolbar.get().setNavigationOnClickListener(v -> NavHostFragment.findNavController(this).popBackStack());
|
||||
toolbar.get().setTitle(R.string.AndroidManifest_archived_conversations);
|
||||
|
||||
|
|
|
@ -141,6 +141,7 @@ import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
|||
import org.thoughtcrime.securesms.util.AppStartup;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.PlayStoreUtil;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
|
||||
|
@ -247,8 +248,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
coordinator = view.findViewById(R.id.coordinator);
|
||||
list = view.findViewById(R.id.list);
|
||||
fab = view.findViewById(R.id.fab);
|
||||
cameraFab = view.findViewById(R.id.camera_fab);
|
||||
searchEmptyState = view.findViewById(R.id.search_no_results);
|
||||
toolbarShadow = view.findViewById(R.id.conversation_list_toolbar_shadow);
|
||||
bottomActionBar = view.findViewById(R.id.conversation_list_bottom_action_bar);
|
||||
|
@ -258,6 +257,17 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
paymentNotificationView = new Stub<>(view.findViewById(R.id.payments_notification));
|
||||
voiceNotePlayerViewStub = new Stub<>(view.findViewById(R.id.voice_note_player));
|
||||
|
||||
if (FeatureFlags.internalUser()) {
|
||||
fab = view.findViewById(R.id.fab_new);
|
||||
cameraFab = view.findViewById(R.id.camera_fab_new);
|
||||
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
cameraFab.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
fab = view.findViewById(R.id.fab_old);
|
||||
cameraFab = view.findViewById(R.id.camera_fab_old);
|
||||
}
|
||||
|
||||
Toolbar toolbar = getToolbar(view);
|
||||
toolbar.setVisibility(View.VISIBLE);
|
||||
|
||||
|
|
|
@ -110,7 +110,10 @@ class MainActivityListHostFragment : Fragment(R.layout.main_activity_list_host_f
|
|||
R.id.action_conversationListFragment_to_storiesLandingFragment,
|
||||
null,
|
||||
null,
|
||||
FragmentNavigatorExtras(requireView().findViewById<View>(R.id.camera_fab) to "camera_fab")
|
||||
FragmentNavigatorExtras(
|
||||
requireView().findViewById<View>(R.id.camera_fab_new) to "camera_fab",
|
||||
requireView().findViewById<View>(R.id.fab_new) to "new_convo_fab"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,15 @@ import android.view.View
|
|||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
|
@ -39,6 +43,7 @@ import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel
|
|||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* The "landing page" for Stories.
|
||||
|
@ -46,7 +51,7 @@ import org.thoughtcrime.securesms.util.visible
|
|||
class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_landing_fragment) {
|
||||
|
||||
private lateinit var emptyNotice: View
|
||||
private lateinit var cameraFab: View
|
||||
private lateinit var cameraFab: FloatingActionButton
|
||||
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
|
@ -86,7 +91,16 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
|||
emptyNotice = requireView().findViewById(R.id.empty_notice)
|
||||
cameraFab = requireView().findViewById(R.id.camera_fab)
|
||||
|
||||
sharedElementEnterTransition = TransitionInflater.from(requireContext()).inflateTransition(R.transition.change_transform)
|
||||
sharedElementEnterTransition = TransitionInflater.from(requireContext()).inflateTransition(R.transition.change_transform_fabs)
|
||||
setEnterSharedElementCallback(object : SharedElementCallback() {
|
||||
override fun onSharedElementStart(sharedElementNames: MutableList<String>?, sharedElements: MutableList<View>?, sharedElementSnapshots: MutableList<View>?) {
|
||||
if (sharedElementNames?.contains("camera_fab") == true) {
|
||||
lifecycleDisposable += Single.timer(200, TimeUnit.MILLISECONDS).subscribeBy {
|
||||
cameraFab.setImageResource(R.drawable.ic_camera_outline_24)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
cameraFab.setOnClickListener {
|
||||
Permissions.with(this)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:controlX1="0.4"
|
||||
android:controlX2="0.0"
|
||||
android:controlY1="0.2"
|
||||
android:controlY2="1" />
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accelerateDecelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
|
|
@ -110,8 +110,7 @@
|
|||
app:layout_behavior="org.thoughtcrime.securesms.util.views.SlideUpWithSnackbarBehavior">
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:transitionName="camera_fab"
|
||||
android:id="@+id/camera_fab"
|
||||
android:id="@+id/camera_fab_new"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
|
@ -119,21 +118,56 @@
|
|||
android:layout_marginBottom="20dp"
|
||||
android:contentDescription="@string/conversation_list_fragment__open_camera_description"
|
||||
android:focusable="true"
|
||||
app:tint="@color/signal_icon_tint_secondary"
|
||||
app:backgroundTint="@color/conversation_list_camera_button_background"
|
||||
app:srcCompat="@drawable/ic_camera_solid_white_24" />
|
||||
android:theme="@style/Widget.Material3.FloatingActionButton.Secondary"
|
||||
android:transitionName="camera_fab"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/signal_colorSecondaryContainer"
|
||||
app:srcCompat="@drawable/ic_camera_outline_24"
|
||||
app:tint="@color/signal_colorOnSecondaryContainer"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:id="@+id/fab_new"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:contentDescription="@string/conversation_list_fragment__fab_content_description"
|
||||
android:focusable="true"
|
||||
app:tint="@color/core_white"
|
||||
android:theme="@style/Widget.Material3.FloatingActionButton.Secondary"
|
||||
android:visibility="gone"
|
||||
android:transitionName="new_convo_fab"
|
||||
app:backgroundTint="@color/signal_colorPrimaryContainer"
|
||||
app:srcCompat="@drawable/ic_compose_outline_24"
|
||||
app:tint="@color/signal_colorOnPrimaryContainer"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:id="@+id/camera_fab_old"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:contentDescription="@string/conversation_list_fragment__open_camera_description"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/conversation_list_camera_button_background"
|
||||
app:srcCompat="@drawable/ic_camera_solid_white_24"
|
||||
app:tint="@color/signal_icon_tint_secondary" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:id="@+id/fab_old"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:contentDescription="@string/conversation_list_fragment__fab_content_description"
|
||||
android:focusable="true"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/core_ultramarine"
|
||||
app:srcCompat="@drawable/ic_compose_solid_24" />
|
||||
app:srcCompat="@drawable/ic_compose_solid_24"
|
||||
app:tint="@color/core_white" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/megaphone_container"
|
||||
|
|
|
@ -24,21 +24,38 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:id="@+id/camera_fab_shared_element_target"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:focusable="true"
|
||||
android:theme="@style/Widget.Material3.FloatingActionButton.Secondary"
|
||||
android:transitionName="camera_fab"
|
||||
app:elevation="0dp"
|
||||
app:backgroundTint="@color/signal_colorSecondaryContainer"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/ic_camera_outline_24"
|
||||
app:tint="@color/signal_colorOnSecondaryContainer" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton
|
||||
android:id="@+id/camera_fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:contentDescription="@string/conversation_list_fragment__open_camera_description"
|
||||
android:focusable="true"
|
||||
android:theme="@style/Widget.Material3.FloatingActionButton.Secondary"
|
||||
android:transitionName="camera_fab"
|
||||
android:theme="@style/Widget.Material3.FloatingActionButton.Primary"
|
||||
android:transitionName="new_convo_fab"
|
||||
app:backgroundTint="@color/signal_colorPrimaryContainer"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:srcCompat="@drawable/ic_camera_outline_24"
|
||||
app:srcCompat="@drawable/ic_compose_outline_24"
|
||||
app:tint="@color/signal_colorOnPrimaryContainer" />
|
||||
|
||||
<TextView
|
||||
|
|
9
app/src/main/res/transition/change_transform_fabs.xml
Normal file
9
app/src/main/res/transition/change_transform_fabs.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<transitionSet android:duration="200"
|
||||
android:interpolator="@anim/camera_fab_cubic_easing_interpolator"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<transition class="org.thoughtcrime.securesms.animation.transitions.FabElevationFadeTransform" />
|
||||
<changeBounds/>
|
||||
<changeTransform/>
|
||||
<changeImageTransform/>
|
||||
</transitionSet>
|
Loading…
Add table
Reference in a new issue