diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/NavigationController.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/NavigationController.kt index 780e82cb..51984810 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/NavigationController.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/NavigationController.kt @@ -123,6 +123,10 @@ class NavigationController @Inject constructor(private val activity: AppCompatAc SessionDetailActivity.start(activity, session) } + fun navigateToSessionDetailActivity(session: Session, sharedElement: Pair) { + SessionDetailActivity.start(activity, session, sharedElement) + } + fun navigateToSessionsFeedbackActivity(session: Session.SpeechSession) { SessionsFeedbackActivity.start(activity, session) } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailActivity.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailActivity.kt index 05e4ab30..3b27ba67 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailActivity.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailActivity.kt @@ -1,14 +1,22 @@ package io.github.droidkaigi.confsched2018.presentation.detail +import android.annotation.TargetApi +import android.app.Activity +import android.app.SharedElementCallback import android.arch.lifecycle.ViewModelProvider import android.arch.lifecycle.ViewModelProviders import android.content.Context import android.content.Intent import android.databinding.DataBindingUtil +import android.os.Build import android.os.Bundle +import android.support.v4.app.ActivityOptionsCompat import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentStatePagerAdapter +import android.support.v4.util.Pair +import android.support.v4.view.ViewPager +import android.view.View import dagger.android.AndroidInjector import dagger.android.DispatchingAndroidInjector import dagger.android.support.HasSupportFragmentInjector @@ -46,8 +54,16 @@ class SessionDetailActivity : private val pagerAdapter = SessionDetailFragmentPagerAdapter(supportFragmentManager) + private lateinit var sessions: List + + val firstSessionId: String by lazy { + intent.getStringExtra(EXTRA_SESSION_ID) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportPostponeEnterTransition() + supportActionBar?.let { it.setDisplayHomeAsUpEnabled(true) it.setDisplayShowTitleEnabled(false) @@ -55,8 +71,11 @@ class SessionDetailActivity : sessionDetailViewModel.sessions.observe(this) { result -> when (result) { is Result.Success -> { - val sessions = result.data + sessions = result.data bindSessions(sessions) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + removeSharedElements() + } } is Result.Failure -> { Timber.e(result.e) @@ -72,8 +91,7 @@ class SessionDetailActivity : val firstAssign = pagerAdapter.sessions.isEmpty() && sessions.isNotEmpty() pagerAdapter.sessions = sessions if (firstAssign) { - val sessionId = intent.getStringExtra(EXTRA_SESSION_ID) - val position = sessions.indexOfFirst { it.id == sessionId } + val position = sessions.indexOfFirst { it.id == firstSessionId } binding .detailSessionsPager .setCurrentItem( @@ -83,6 +101,23 @@ class SessionDetailActivity : } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private fun removeSharedElements() { + setEnterSharedElementCallback(object : SharedElementCallback() { + override fun onMapSharedElements( + names: MutableList?, + sharedElements: MutableMap?) { + super.onMapSharedElements(names, sharedElements) + + val currentFragment = pagerAdapter.findFragmentByPosition( + binding.detailSessionsPager, + binding.detailSessionsPager.currentItem) + sharedElements?.clear() + currentFragment.hideButton() + } + }) + } + override fun supportFragmentInjector(): AndroidInjector = dispatchingAndroidInjector override fun onBackPressed() { @@ -102,6 +137,7 @@ class SessionDetailActivity : class SessionDetailFragmentPagerAdapter( fragmentManager: FragmentManager ) : FragmentStatePagerAdapter(fragmentManager) { + var sessions: List = listOf() set(value) { field = value @@ -109,22 +145,45 @@ class SessionDetailActivity : } override fun getItem(position: Int): Fragment { - return SessionDetailFragment.newInstance(sessions[position].id) + return SessionDetailFragment.newInstance(sessions[position].id, sessions[position].id) } override fun getCount(): Int = sessions.size + + fun findFragmentByPosition(viewPager: ViewPager, position: Int): SessionDetailFragment { + return instantiateItem(viewPager, position) as SessionDetailFragment + } } companion object { const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID" + private const val EXTRA_TRANSITION_NAME = "EXTRA_TRANSITION_NAME" + fun start(context: Context, session: Session) { context.startActivity(createIntent(context, session.id)) } + fun start(activity: Activity, session: Session, sharedElement: Pair) { + val options = ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, + sharedElement) + val intent = createIntent(activity, session.id, sharedElement) + activity.startActivity(intent, options.toBundle()) + } + fun createIntent(context: Context, sessionId: String): Intent { return Intent(context, SessionDetailActivity::class.java).apply { putExtra(EXTRA_SESSION_ID, sessionId) } } + + private fun createIntent(context: Context, sessionId: String, sharedElement: Pair): + Intent { + return Intent(context, SessionDetailActivity::class.java).apply { + putExtra(EXTRA_SESSION_ID, sessionId) + putExtra(EXTRA_TRANSITION_NAME, sharedElement.second) + } + } } } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailFragment.kt index 6b2126a2..2c88ae8b 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/detail/SessionDetailFragment.kt @@ -1,10 +1,14 @@ package io.github.droidkaigi.confsched2018.presentation.detail +import android.annotation.TargetApi import android.arch.lifecycle.ViewModelProvider import android.arch.lifecycle.ViewModelProviders import android.graphics.drawable.Animatable +import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment +import android.support.v4.view.ViewCompat +import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -16,6 +20,7 @@ import io.github.droidkaigi.confsched2018.model.Session import io.github.droidkaigi.confsched2018.presentation.NavigationController import io.github.droidkaigi.confsched2018.presentation.Result import io.github.droidkaigi.confsched2018.util.SessionAlarm +import io.github.droidkaigi.confsched2018.util.ext.addOnetimeOnPreDrawListener import io.github.droidkaigi.confsched2018.util.ext.context import io.github.droidkaigi.confsched2018.util.ext.drawable import io.github.droidkaigi.confsched2018.util.ext.observe @@ -75,6 +80,15 @@ class SessionDetailFragment : Fragment(), Injectable { } binding.toolbar.setNavigationOnClickListener { activity?.finish() } + + val firstSessionId = (activity as? SessionDetailActivity)?.firstSessionId ?: return + val transitionName = arguments!!.getString(EXTRA_TRANSITION_NAME) + + if (!TextUtils.isEmpty(transitionName) + && firstSessionId == transitionName + && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + initViewTransitions(view) + } } private fun bindSession(session: Session.SpeechSession) { @@ -110,6 +124,20 @@ class SessionDetailFragment : Fragment(), Injectable { binding.nextSession = nextSession } + fun hideButton() { + binding.fab.visibility = View.INVISIBLE + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private fun initViewTransitions(view: View) { + binding.speakerSummary.addOnetimeOnPreDrawListener { + ViewCompat.setTransitionName( + view.findViewById(R.id.speaker_summary), + arguments!!.getString(EXTRA_TRANSITION_NAME)) + activity?.supportStartPostponedEnterTransition() + } + } + interface OnClickBottomAreaListener { fun onClickPrevSession() fun onClickNextSession() @@ -117,10 +145,20 @@ class SessionDetailFragment : Fragment(), Injectable { companion object { const val EXTRA_SESSION_ID = "EXTRA_SESSION_ID" + const val EXTRA_TRANSITION_NAME = "EXTRA_TRANSITION_NAME" + fun newInstance(sessionId: String): SessionDetailFragment = SessionDetailFragment().apply { arguments = Bundle().apply { putString(EXTRA_SESSION_ID, sessionId) } } + + fun newInstance(sessionId: String, transitionName: String): SessionDetailFragment = + SessionDetailFragment().apply { + arguments = Bundle().apply { + putString(EXTRA_SESSION_ID, sessionId) + putString(EXTRA_TRANSITION_NAME, transitionName) + } + } } } diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/favorite/FavoriteSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/favorite/FavoriteSessionsFragment.kt index c4a5714b..b55503b2 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/favorite/FavoriteSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/favorite/FavoriteSessionsFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.support.transition.TransitionInflater import android.support.transition.TransitionManager import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.SimpleItemAnimator @@ -91,9 +92,14 @@ class FavoriteSessionsFragment : Fragment(), Injectable { private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener({ item, _ -> + setOnItemClickListener({ item, v -> val sessionItem = item as? SpeechSessionItem ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) }) } binding.sessionsRecycler.apply { diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/search/SearchSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/search/SearchSessionsFragment.kt index 6edb2225..4babe8e6 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/search/SearchSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/search/SearchSessionsFragment.kt @@ -4,11 +4,13 @@ import android.arch.lifecycle.ViewModelProvider import android.arch.lifecycle.ViewModelProviders import android.os.Bundle import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.xwray.groupie.GroupAdapter import com.xwray.groupie.ViewHolder +import io.github.droidkaigi.confsched2018.R import io.github.droidkaigi.confsched2018.databinding.FragmentSearchSessionsBinding import io.github.droidkaigi.confsched2018.di.Injectable import io.github.droidkaigi.confsched2018.model.Session @@ -70,9 +72,14 @@ class SearchSessionsFragment : Fragment(), Injectable, TabLayoutItem { private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener({ item, _ -> + setOnItemClickListener({ item, v -> val sessionItem = (item as? HorizontalSessionItem) ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) }) } binding.searchSessionRecycler.apply { diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt index 082d0075..e85c700d 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/AllSessionsFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.support.transition.TransitionInflater import android.support.transition.TransitionManager import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.SimpleItemAnimator @@ -112,9 +113,14 @@ class AllSessionsFragment : Fragment(), Injectable, CurrentSessionScroller, TabL private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener({ item, _ -> + setOnItemClickListener({ item, v -> val sessionItem = item as? SpeechSessionItem ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) }) } binding.sessionsRecycler.apply { diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt index d81ee826..f1f023fd 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/sessions/RoomSessionsFragment.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.support.transition.TransitionInflater import android.support.transition.TransitionManager import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.SimpleItemAnimator @@ -120,9 +121,14 @@ class RoomSessionsFragment : Fragment(), Injectable, CurrentSessionScroller, Tab private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener({ item, _ -> + setOnItemClickListener({ item, v -> val sessionItem = item as? SpeechSessionItem ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) }) } binding.sessionsRecycler.apply { diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/speaker/SpeakerDetailFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/speaker/SpeakerDetailFragment.kt index 0e4819f7..87da30d2 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/speaker/SpeakerDetailFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/speaker/SpeakerDetailFragment.kt @@ -9,6 +9,7 @@ import android.os.Build import android.os.Bundle import android.support.design.widget.BottomSheetDialog import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.support.v4.view.ViewCompat import android.support.v7.widget.LinearLayoutManager import android.text.TextUtils @@ -214,9 +215,14 @@ class SpeakerDetailFragment : Fragment(), Injectable { private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener({ item, _ -> + setOnItemClickListener({ item, v -> val sessionItem = item as? SpeechSessionItem ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) }) } val linearLayoutManager = LinearLayoutManager(context) diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/topic/TopicDetailFragment.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/topic/TopicDetailFragment.kt index 61230d03..66917b71 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/topic/TopicDetailFragment.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/presentation/topic/TopicDetailFragment.kt @@ -4,6 +4,7 @@ import android.arch.lifecycle.ViewModelProvider import android.arch.lifecycle.ViewModelProviders import android.os.Bundle import android.support.v4.app.Fragment +import android.support.v4.util.Pair import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View @@ -78,9 +79,14 @@ class TopicDetailFragment : Fragment(), Injectable { private fun setupRecyclerView() { val groupAdapter = GroupAdapter().apply { add(sessionsSection) - setOnItemClickListener { item, _ -> + setOnItemClickListener { item, v -> val sessionItem = item as? SpeechSessionItem ?: return@setOnItemClickListener - navigationController.navigateToSessionDetailActivity(sessionItem.session) + val sharedElement = Pair( + v.findViewById(R.id.speaker_summary), + sessionItem.session.id) + navigationController.navigateToSessionDetailActivity( + sessionItem.session, + sharedElement) } } val linearLayoutManager = LinearLayoutManager(context) diff --git a/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/ViewExt.kt b/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/ViewExt.kt index 627b5f1b..c3b3a71b 100644 --- a/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/ViewExt.kt +++ b/app/src/main/java/io/github/droidkaigi/confsched2018/util/ext/ViewExt.kt @@ -2,6 +2,7 @@ package io.github.droidkaigi.confsched2018.util.ext import android.os.Build import android.view.View +import android.view.ViewTreeObserver fun View.setVisible(visible: Boolean) { if (visible) { @@ -23,6 +24,17 @@ fun View.isGone() = visibility == View.GONE fun View.isVisible() = visibility == View.VISIBLE +fun View.addOnetimeOnPreDrawListener(listener: () -> Unit) { + viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + viewTreeObserver.removeOnPreDrawListener(this) + listener.invoke() + + return true + } + }) +} + var View.elevationForPostLollipop: Float get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { elevation diff --git a/app/src/main/res/layout/fragment_session_detail.xml b/app/src/main/res/layout/fragment_session_detail.xml index b82ad23b..5b95e3fe 100644 --- a/app/src/main/res/layout/fragment_session_detail.xml +++ b/app/src/main/res/layout/fragment_session_detail.xml @@ -121,6 +121,7 @@ tools:text="初心者向け" />