1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wallpaper.picker.preview.ui.binder
17 
18 import android.annotation.SuppressLint
19 import android.content.Context
20 import android.graphics.Point
21 import android.view.View
22 import androidx.core.view.doOnLayout
23 import androidx.core.view.doOnPreDraw
24 import androidx.lifecycle.Lifecycle
25 import androidx.lifecycle.LifecycleOwner
26 import androidx.lifecycle.lifecycleScope
27 import androidx.lifecycle.repeatOnLifecycle
28 import androidx.recyclerview.widget.RecyclerView
29 import androidx.transition.Transition
30 import androidx.viewpager2.widget.ViewPager2
31 import com.android.wallpaper.R
32 import com.android.wallpaper.config.BaseFlags
33 import com.android.wallpaper.model.wallpaper.DeviceDisplayType
34 import com.android.wallpaper.picker.customization.ui.view.transformer.PreviewPagerPageTransformer
35 import com.android.wallpaper.picker.preview.ui.view.adapters.SinglePreviewPagerAdapter
36 import com.android.wallpaper.picker.preview.ui.view.pagetransformers.PreviewCardPageTransformer
37 import com.android.wallpaper.picker.preview.ui.viewmodel.FullPreviewConfigViewModel
38 import com.android.wallpaper.picker.preview.ui.viewmodel.WallpaperPreviewViewModel
39 import com.android.wallpaper.util.wallpaperconnection.WallpaperConnectionUtils
40 import kotlinx.coroutines.CompletableDeferred
41 import kotlinx.coroutines.CoroutineScope
42 import kotlinx.coroutines.launch
43 
44 /** Binds single preview home screen and lock screen tabs view pager. */
45 object PreviewPagerBinder {
46 
47     @SuppressLint("WrongConstant")
48     fun bind(
49         applicationContext: Context,
50         mainScope: CoroutineScope,
51         viewLifecycleOwner: LifecycleOwner,
52         previewsViewPager: ViewPager2,
53         wallpaperPreviewViewModel: WallpaperPreviewViewModel,
54         previewDisplaySize: Point,
55         transition: Transition?,
56         transitionConfig: FullPreviewConfigViewModel?,
57         wallpaperConnectionUtils: WallpaperConnectionUtils,
58         isFirstBindingDeferred: CompletableDeferred<Boolean>,
59         navigate: (View) -> Unit,
60     ) {
61         previewsViewPager.apply {
62             adapter = SinglePreviewPagerAdapter { viewHolder, position ->
63                 PreviewTooltipBinder.bindSmallPreviewTooltip(
64                     tooltipStub =
65                         viewHolder.itemView.requireViewById(R.id.small_preview_tooltip_stub),
66                     viewModel = wallpaperPreviewViewModel.smallTooltipViewModel,
67                     lifecycleOwner = viewLifecycleOwner,
68                 )
69 
70                 SmallPreviewBinder.bind(
71                     applicationContext = applicationContext,
72                     view = viewHolder.itemView.requireViewById(R.id.preview),
73                     viewModel = wallpaperPreviewViewModel,
74                     screen = wallpaperPreviewViewModel.smallPreviewTabs[position],
75                     displaySize = previewDisplaySize,
76                     deviceDisplayType = DeviceDisplayType.SINGLE,
77                     mainScope = mainScope,
78                     viewLifecycleOwner = viewLifecycleOwner,
79                     currentNavDestId = R.id.smallPreviewFragment,
80                     transition = transition,
81                     transitionConfig = transitionConfig,
82                     isFirstBindingDeferred = isFirstBindingDeferred,
83                     wallpaperConnectionUtils = wallpaperConnectionUtils,
84                     navigate = navigate,
85                 )
86             }
87             offscreenPageLimit = SinglePreviewPagerAdapter.PREVIEW_PAGER_ITEM_COUNT
88             // the over scroll animation needs to be disabled for the RecyclerView that is contained
89             // in the ViewPager2 rather than the ViewPager2 itself
90             val child: View = getChildAt(0)
91             if (child is RecyclerView) {
92                 child.overScrollMode = View.OVER_SCROLL_NEVER
93                 // Remove clip children to enable child card view to display fully during scaling
94                 // shared element transition.
95                 child.clipChildren = false
96             }
97 
98             // When pager's height changes, request transform to recalculate the preview offset
99             // to make sure correct space between the previews.
100             // TODO (b/348462236): figure out how to scale surface view content with layout change
101             addOnLayoutChangeListener { view, _, _, _, _, _, topWas, _, bottomWas ->
102                 val isHeightChanged = (bottomWas - topWas) != view.height
103                 if (isHeightChanged) {
104                     requestTransform()
105                 }
106             }
107         }
108 
109         // Only when pager is laid out, we can get the width and set the preview's offset correctly
110         previewsViewPager.doOnLayout {
111             val pageTransformer =
112                 if (BaseFlags.get().isNewPickerUi()) PreviewPagerPageTransformer(previewDisplaySize)
113                 else PreviewCardPageTransformer(previewDisplaySize)
114             (it as ViewPager2).setPageTransformer(pageTransformer)
115         }
116 
117         // Wrap in doOnPreDraw for emoji wallpaper creation case, to make sure recycler view with
118         // previews have finished layout before calling registerOnPageChangeCallback and
119         // setCurrentItem.
120         // TODO (b/339679893): investigate to see if there is a better solution
121         previewsViewPager.doOnPreDraw {
122             previewsViewPager.registerOnPageChangeCallback(
123                 object : ViewPager2.OnPageChangeCallback() {
124                     override fun onPageSelected(position: Int) {
125                         super.onPageSelected(position)
126                         wallpaperPreviewViewModel.setSmallPreviewSelectedTabIndex(position)
127                     }
128                 }
129             )
130 
131             viewLifecycleOwner.lifecycleScope.launch {
132                 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
133                     wallpaperPreviewViewModel.smallPreviewSelectedTabIndex.collect {
134                         if (previewsViewPager.currentItem != it) {
135                             previewsViewPager.setCurrentItem(it, /* smoothScroll= */ true)
136                         }
137                     }
138                 }
139             }
140         }
141     }
142 }
143