Catalog Jetpack Compose refactor (#57)

* Updated gradle dependencies to support jetpack compose

* Added Catalog screen and ViewPager component

* Added catalog pages composable functions

* Added coil dependency

* Move some dependendies from androidx to compose dependencies

* Use composeUiVersion in build.gradle instead of harcoded value

* Added missing Copyright

* Move padding to BottomPageNumber composable function

* Created GuitarImage composable function

* Added pager state saver to preserve the state when configuration changes occurs

* Fixed lint issue

* Updated Dagger Hilt version from 2.39.1 to 2.42 and removed lintOptions

* Added PageLayout composable to hold page ui

* Updated ui to support small width devices

* Removed background color for seventh page

* Added selectPageNumber function in order to jump to a specific page number

* Replaced hardcoded values for destinationPage

* Updated Copyright for ViewPager class

* Added page number as last character for ContentTextItem

* Removed all files related to Catalog UI View System

* Avoid adding one more page for view pager when the current page is max page
and configuration change occurs
This commit is contained in:
Alin-Mihai Argeanu 2022-06-14 20:33:04 +03:00 коммит произвёл GitHub
Родитель 73d62cf1cf
Коммит b27b34168d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
43 изменённых файлов: 2680 добавлений и 2339 удалений

Просмотреть файл

@ -75,6 +75,11 @@ android {
buildFeatures {
dataBinding true
viewBinding true
compose true
}
composeOptions {
kotlinCompilerExtensionVersion rootProject.ext.composeUiVersion
}
configurations.all {
@ -109,6 +114,17 @@ dependencies {
implementation navigationDependencies.fragmentKtx
implementation navigationDependencies.uiKtx
implementation composeDependencies.composeUi
debugImplementation composeDependencies.composeTooling
implementation composeDependencies.composeFoundation
implementation composeDependencies.composeMaterial
implementation composeDependencies.composeMaterialIcons
implementation composeDependencies.composeMaterialIconsExtended
implementation composeDependencies.composeViewModel
implementation composeDependencies.composeLiveData
implementation composeDependencies.constraintlayoutCompose
implementation composeDependencies.coil
implementation googleDependencies.ossLicenses
implementation googleDependencies.material
implementation googleDependencies.gson
@ -127,6 +143,7 @@ dependencies {
implementation microsoftDependencies.layouts
implementation microsoftDependencies.wmUtils
implementation microsoftDependencies.snackbar
implementation microsoftDependencies.windowState
implementation uiDependencies.lottie

Просмотреть файл

@ -1,75 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiDevice
import com.microsoft.device.dualscreen.testing.resetOrientation
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.presentation.MainActivity
import com.microsoft.device.samples.dualscreenexperience.presentation.store.checkToolbar
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.After
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
@HiltAndroidTest
class CatalogNavigationDualScreenTest {
private val activityRule = ActivityTestRule(MainActivity::class.java)
private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@get:Rule
var ruleChain: RuleChain =
RuleChain.outerRule(HiltAndroidRule(this)).around(activityRule)
@After
fun resetOrientation() {
device.resetOrientation()
}
@Test
fun checkAllCatalogItems() {
navigateToCatalogSection()
checkToolbar(R.string.toolbar_catalog_title)
checkCatalogPageIsDisplayed(1)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(2)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(3)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(4)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(5)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(6)
swipeCatalogViewPagerToTheLeft()
checkCatalogPageIsDisplayed(7)
checkToolbar(R.string.toolbar_catalog_title)
}
@Test
fun checkAllCatalogItemsAfterRotation() {
navigateToCatalogSection()
device.setOrientationRight()
checkAllCatalogItems()
}
}

Просмотреть файл

@ -1,35 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.swipeLeft
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.util.forceClick
import org.hamcrest.core.AllOf.allOf
fun navigateToCatalogSection() {
onView(withId(R.id.navigation_catalog_graph)).perform(forceClick())
}
fun checkCatalogPageIsDisplayed(pageNo: Int) {
onView(
allOf(
withId(R.id.pages),
withText("Page $pageNo of 7")
)
).check(matches(isDisplayed()))
}
fun swipeCatalogViewPagerToTheLeft() {
onView(withId(R.id.pager)).perform(swipeLeft())
}

Просмотреть файл

@ -264,10 +264,10 @@ class MainActivity : AppCompatActivity() {
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
if (layoutInfoViewModel.isDualMode.value == true) {
menuInflater.inflate(R.menu.main_menu, menu)
menu?.findItem(R.id.menu_main_dev_mode)?.actionView?.apply {
menu.findItem(R.id.menu_main_dev_mode)?.actionView?.apply {
setOnClickListener {
onDevModeClicked(it)
}

Просмотреть файл

@ -1,61 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page1
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page2
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page3
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page4
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page5
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page6
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage.Page7
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage1Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage2Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage3Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage4Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage5Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage6Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item.CatalogPage7Fragment
import com.microsoft.device.samples.dualscreenexperience.presentation.util.DataListProvider
internal class CatalogListAdapter(
fragmentManager: FragmentManager,
private val dataProvider: DataListProvider<CatalogItem>
) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
var showTwoPages = false
override fun getCount(): Int = dataProvider.getDataList()?.size ?: 0
override fun getItem(position: Int): Fragment {
dataProvider.getDataList()?.get(position)?.let { item ->
return when (item.viewType) {
Page1 -> CatalogPage1Fragment()
Page2 -> CatalogPage2Fragment()
Page3 -> CatalogPage3Fragment()
Page4 -> CatalogPage4Fragment()
Page5 -> CatalogPage5Fragment()
Page6 -> CatalogPage6Fragment()
Page7 -> CatalogPage7Fragment()
}
}
throw RuntimeException("Catalog Type Error")
}
fun refreshData() {
notifyDataSetChanged()
}
override fun getPageWidth(position: Int): Float {
// 0.5f : Each page occupies full space
// 1.0f : Each page occupies half space
return if (showTwoPages) 0.5f else 1.0f
}
}

Просмотреть файл

@ -2,7 +2,6 @@
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog
@ -13,36 +12,39 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.viewpager.widget.ViewPager
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.microsoft.device.dualscreen.utils.wm.getFoldingFeature
import com.microsoft.device.dualscreen.utils.wm.isFoldingFeatureVertical
import com.microsoft.device.dualscreen.utils.wm.isInDualMode
import com.microsoft.device.dualscreen.windowstate.FoldState
import com.microsoft.device.dualscreen.windowstate.rememberWindowState
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogBinding
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.theme.CatalogTheme
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view.Catalog
import com.microsoft.device.samples.dualscreenexperience.presentation.util.appCompatActivity
import com.microsoft.device.samples.dualscreenexperience.presentation.util.changeToolbarTitle
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.isFoldOrSmallHinge
import com.microsoft.device.samples.dualscreenexperience.presentation.util.isFragmentWidthSmall
import com.microsoft.device.samples.dualscreenexperience.presentation.util.setupToolbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@AndroidEntryPoint
class CatalogListFragment : Fragment(), ViewPager.OnPageChangeListener {
class CatalogListFragment : Fragment() {
private val viewModel: CatalogListViewModel by activityViewModels()
private var binding: FragmentCatalogBinding? = null
private var catalogAdapter: CatalogListAdapter? = null
override fun onAttach(context: Context) {
super.onAttach(context)
@ -68,36 +70,36 @@ class CatalogListFragment : Fragment(), ViewPager.OnPageChangeListener {
): View? {
binding = FragmentCatalogBinding.inflate(inflater, container, false)
binding?.lifecycleOwner = this
return binding?.root
}
binding?.composeView?.apply {
// Dispose of the Composition when the view's LifecycleOwner
// is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
CatalogTheme {
val windowState = appCompatActivity?.rememberWindowState()
if (windowState != null) {
val isFeatureFoldHorizontal =
windowState.hasFold && windowState.foldIsHorizontal
val isFoldStateHalfOpened = windowState.foldState == FoldState.HALF_OPENED
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupAdapter()
setupObservers()
}
private fun setupAdapter() {
catalogAdapter = CatalogListAdapter(childFragmentManager, viewModel)
}
private fun setupViewPager() {
binding?.pager?.apply {
adapter = catalogAdapter
currentItem = viewModel.catalogItemPosition.value ?: 0
addOnPageChangeListener(this@CatalogListFragment)
}
}
private fun setupObservers() {
viewModel.catalogItemList.observe(viewLifecycleOwner) { catalogAdapter?.refreshData() }
viewModel.catalogItemPosition.observe(viewLifecycleOwner) { newPageNumber ->
binding?.pager?.apply {
if (currentItem != newPageNumber) {
setCurrentItem(newPageNumber, true)
Catalog(
pane1WidthDp = windowState.pane1SizeDp.width,
pane2WidthDp = windowState.pane2SizeDp.width,
foldSizeDp = windowState.foldSizeDp,
isFeatureHorizontal = isFeatureFoldHorizontal,
isSinglePortrait = windowState.isSinglePortrait(),
showTwoPages = viewModel.showTwoPages.observeAsState().value ?: false,
showSmallWindowWidthLayout =
viewModel.showSmallWindowWidthLayout.observeAsState().value ?: false,
isFoldStateHalfOpened = isFoldStateHalfOpened,
catalogList =
viewModel.catalogItemList.observeAsState().value ?: listOf()
)
}
}
}
}
return binding?.root
}
override fun onResume() {
@ -113,30 +115,23 @@ class CatalogListFragment : Fragment(), ViewPager.OnPageChangeListener {
private fun onWindowLayoutInfoChanged(windowLayoutInfo: WindowLayoutInfo) {
val isLargeScreenOrHasHinge = activity?.hasExpandedWindowLayoutSize() == true ||
windowLayoutInfo.getFoldingFeature()?.isFoldOrSmallHinge() == false
catalogAdapter?.showTwoPages = windowLayoutInfo.isInDualMode() &&
val shouldShowTwoPages = windowLayoutInfo.isInDualMode() &&
windowLayoutInfo.isFoldingFeatureVertical() &&
isLargeScreenOrHasHinge
viewModel.showTwoPages.value = catalogAdapter?.showTwoPages ?: false
val shouldShowSmallWindowWidthLayout =
appCompatActivity?.isFragmentWidthSmall(
windowLayoutInfo.getFoldingFeature(),
shouldShowTwoPages
)?.takeIf {
it && !shouldShowTwoPages
} ?: false
setupViewPager()
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
// do nothing
}
override fun onPageSelected(position: Int) {
viewModel.catalogItemPosition.value = position
}
override fun onPageScrollStateChanged(state: Int) {
// do nothing
viewModel.updateShowTwoPages(shouldShowTwoPages)
viewModel.updateShowSmallWindowWidthLayout(shouldShowSmallWindowWidthLayout)
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
catalogAdapter = null
}
}

Просмотреть файл

@ -7,6 +7,7 @@
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@ -23,7 +24,14 @@ class CatalogListViewModel @Inject constructor(
) : ViewModel(), DataListProvider<CatalogItem> {
var catalogItemList = MutableLiveData<List<CatalogItem>?>(null)
var catalogItemPosition = MutableLiveData(0)
var showTwoPages = MutableLiveData(false)
private val _showTwoPages = MutableLiveData(false)
val showTwoPages: LiveData<Boolean>
get() = _showTwoPages
private val _showSmallWindowWidthLayout = MutableLiveData(false)
val showSmallWindowWidthLayout: LiveData<Boolean>
get() = _showSmallWindowWidthLayout
init {
viewModelScope.launch {
@ -31,5 +39,13 @@ class CatalogListViewModel @Inject constructor(
}
}
fun updateShowTwoPages(showTwoPages: Boolean) {
_showTwoPages.value = showTwoPages
}
fun updateShowSmallWindowWidthLayout(showSmallWindowWidthLayout: Boolean) {
_showSmallWindowWidthLayout.value = showSmallWindowWidthLayout
}
override fun getDataList(): List<CatalogItem>? = catalogItemList.value
}

Просмотреть файл

@ -1,132 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage1Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage1Fragment : Fragment() {
private val viewModel: CatalogListViewModel by activityViewModels()
private var binding: FragmentCatalogItemPage1Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage1Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page1.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page1.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupClickListeners()
updateUI()
}
private fun setupClickListeners() {
binding?.textSecond?.setOnClickListener {
viewModel.catalogItemPosition.value = CatalogPage.Page2.ordinal
}
binding?.textThird?.setOnClickListener {
viewModel.catalogItemPosition.value = CatalogPage.Page3.ordinal
}
binding?.textFourth?.setOnClickListener {
viewModel.catalogItemPosition.value = CatalogPage.Page4.ordinal
}
binding?.textFifth?.setOnClickListener {
viewModel.catalogItemPosition.value = CatalogPage.Page6.ordinal
}
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text18sp = resources.getDimension(R.dimen.text_size_18)
val text20sp = resources.getDimension(R.dimen.text_size_20)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text20sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text18sp)
binding?.textThird?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text18sp)
binding?.textFourth?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text18sp)
binding?.textFifth?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text18sp)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem1Layout ?: return
val set = ConstraintSet()
set.clone(constraintLayout)
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
set.connect(
R.id.catalog_item_scroll_view, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,147 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage2Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.isFragmentWidthSmall
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage2Fragment : Fragment() {
private var binding: FragmentCatalogItemPage2Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage2Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page2.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page2.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
activity?.isFragmentWidthSmall(currentFoldingFeature, showsTwoPages)?.takeIf {
it && !showsTwoPages
}?.let {
alignLayoutToSmallWindowWidth()
}
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text20sp = resources.getDimension(R.dimen.text_size_20)
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text20sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
binding?.textThird?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToSmallWindowWidth() {
val constraintLayout = binding?.catalogItem2Layout ?: return
val set = ConstraintSet()
set.clone(constraintLayout)
set.connect(
R.id.text_first, ConstraintSet.BOTTOM,
R.id.text_second, ConstraintSet.TOP, 0
)
binding?.catalogFlow?.setMaxElementsWrap(1)
val catalogFlowArray = IntArray(2).apply {
this[0] = R.id.text_second
this[1] = R.id.image_first
}
binding?.catalogFlow?.referencedIds = catalogFlowArray
set.applyTo(constraintLayout)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem2Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
set.connect(
R.id.catalog_flow, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_third, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,188 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage3Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.isFragmentWidthSmall
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage3Fragment : Fragment() {
private var binding: FragmentCatalogItemPage3Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage3Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page3.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page3.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
activity?.isFragmentWidthSmall(currentFoldingFeature, showsTwoPages)?.takeIf {
it && !showsTwoPages
}?.let {
alignLayoutToSmallWindowWidth()
}
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text20sp = resources.getDimension(R.dimen.text_size_20)
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text20sp)
binding?.textThird?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToSmallWindowWidth() {
val constraintLayout = binding?.catalogItem3Layout ?: return
val set = ConstraintSet()
set.clone(constraintLayout)
val margin = resources.getDimension(R.dimen.normal_margin).toInt()
val marginHorizontal = resources.getDimension(R.dimen.catalog_horizontal_margin).toInt()
set.connect(
R.id.image_first, ConstraintSet.END,
R.id.catalog_item3_layout, ConstraintSet.END, marginHorizontal
)
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.text_first, ConstraintSet.TOP, margin
)
set.connect(
R.id.text_first, ConstraintSet.TOP,
R.id.image_first, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.text_first, ConstraintSet.START,
R.id.catalog_item3_layout, ConstraintSet.START, marginHorizontal
)
set.connect(
R.id.image_second, ConstraintSet.TOP,
R.id.text_first, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.image_second, ConstraintSet.BOTTOM,
R.id.text_second, ConstraintSet.TOP, margin
)
set.connect(
R.id.image_second, ConstraintSet.START,
R.id.catalog_item3_layout, ConstraintSet.START, marginHorizontal
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.image_second, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.text_third, ConstraintSet.END,
R.id.catalog_item3_layout, ConstraintSet.END, marginHorizontal
)
set.applyTo(constraintLayout)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem3Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
if (activity?.isFragmentWidthSmall(foldingFeature, false) == false) {
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
} else {
set.connect(
R.id.image_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
}
set.connect(
R.id.text_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,117 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage4Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage4Fragment : Fragment() {
private var binding: FragmentCatalogItemPage4Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage4Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page4.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page4.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text20sp = resources.getDimension(R.dimen.text_size_20)
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text20sp)
binding?.textThird?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem4Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
set.connect(
R.id.text_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,123 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage5Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage5Fragment : Fragment() {
private var binding: FragmentCatalogItemPage5Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage5Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page5.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page5.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem5Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.image_second, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.END,
R.id.catalog_item5_layout, ConstraintSet.END, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,116 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage6Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage6Fragment : Fragment() {
private var binding: FragmentCatalogItemPage6Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage6Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page6.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page6.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text20sp = resources.getDimension(R.dimen.text_size_20)
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text20sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem6Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -1,184 +0,0 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.item
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.window.layout.FoldingFeature
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.databinding.FragmentCatalogItemPage7Binding
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.CatalogListViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.LayoutInfoViewModel
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getBoundsInWindow
import com.microsoft.device.samples.dualscreenexperience.presentation.util.hasExpandedWindowLayoutSize
import com.microsoft.device.samples.dualscreenexperience.presentation.util.isFragmentWidthSmall
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
class CatalogPage7Fragment : Fragment() {
private var binding: FragmentCatalogItemPage7Binding? = null
private val layoutInfoViewModel: LayoutInfoViewModel by activityViewModels()
private val catalogViewModel: CatalogListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentCatalogItemPage7Binding.inflate(inflater, container, false)
binding?.catalogItem = catalogViewModel.catalogItemList.value?.get(CatalogPage.Page7.ordinal)
binding?.pageNumber = resources.getString(
R.string.catalog_page_no,
CatalogPage.Page7.ordinal + 1,
catalogViewModel.getDataList().sizeOrZero()
)
binding?.lifecycleOwner = this
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
updateUI()
}
private fun updateUI() {
val showsTwoPages = catalogViewModel.showTwoPages.value == true
val isDualMode = layoutInfoViewModel.isDualMode.value == true
val currentFoldingFeature = layoutInfoViewModel.foldingFeature.value
val isExpandedWindowSize = activity?.hasExpandedWindowLayoutSize() == true
activity?.isFragmentWidthSmall(currentFoldingFeature, showsTwoPages)?.takeIf {
it && !showsTwoPages
}?.let {
alignLayoutToSmallWindowWidth()
}
currentFoldingFeature?.takeIf {
isDualMode && !showsTwoPages && it.orientation == FoldingFeature.Orientation.HORIZONTAL
}?.let {
alignLayoutToHorizontalFoldingFeature(it)
if (isExpandedWindowSize) {
updateText()
}
}
}
private fun updateText() {
val text16sp = resources.getDimension(R.dimen.text_size_16)
binding?.textFirst?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
binding?.textSecond?.setTextSize(TypedValue.COMPLEX_UNIT_PX, text16sp)
}
private fun alignLayoutToSmallWindowWidth() {
val constraintLayout = binding?.catalogItem7Layout ?: return
val set = ConstraintSet()
set.clone(constraintLayout)
val margin = resources.getDimension(R.dimen.normal_margin).toInt()
val marginHorizontal = resources.getDimension(R.dimen.catalog_horizontal_margin).toInt()
set.connect(
R.id.image_first, ConstraintSet.END,
R.id.catalog_item7_layout, ConstraintSet.END, marginHorizontal
)
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.text_first, ConstraintSet.TOP, margin
)
set.connect(
R.id.text_first, ConstraintSet.TOP,
R.id.image_first, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.text_first, ConstraintSet.START,
R.id.catalog_item7_layout, ConstraintSet.START, marginHorizontal
)
set.connect(
R.id.image_second, ConstraintSet.TOP,
R.id.text_first, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.image_second, ConstraintSet.BOTTOM,
R.id.text_second, ConstraintSet.TOP, margin
)
set.connect(
R.id.image_second, ConstraintSet.START,
R.id.catalog_item7_layout, ConstraintSet.START, marginHorizontal
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.image_second, ConstraintSet.BOTTOM, margin
)
set.connect(
R.id.text_second, ConstraintSet.END,
R.id.catalog_item7_layout, ConstraintSet.END, marginHorizontal
)
set.applyTo(constraintLayout)
}
private fun alignLayoutToHorizontalFoldingFeature(foldingFeature: FoldingFeature) {
val constraintLayout = binding?.catalogItem7Layout ?: return
foldingFeature.getBoundsInWindow(binding?.root)?.let { rect ->
val horizontalFoldingFeatureHeight = rect.height().coerceAtLeast(1)
val set = ConstraintSet()
set.clone(constraintLayout)
set.constrainHeight(R.id.horizontal_fold, horizontalFoldingFeatureHeight)
set.connect(
R.id.horizontal_fold, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START, 0
)
set.connect(
R.id.horizontal_fold, ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP, 0
)
val foldingMargin = resources.getDimension(R.dimen.small_margin).toInt()
set.setMargin(R.id.horizontal_fold, ConstraintSet.TOP, rect.top)
if (activity?.isFragmentWidthSmall(foldingFeature, false) == false) {
set.connect(
R.id.image_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.connect(
R.id.text_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
}
set.connect(
R.id.image_second, ConstraintSet.TOP,
R.id.horizontal_fold, ConstraintSet.BOTTOM, foldingMargin
)
set.connect(
R.id.text_first, ConstraintSet.BOTTOM,
R.id.horizontal_fold, ConstraintSet.TOP, foldingMargin
)
set.setVisibility(R.id.horizontal_fold, View.INVISIBLE)
set.applyTo(constraintLayout)
}
}
}

Просмотреть файл

@ -0,0 +1,15 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.theme
import androidx.compose.ui.graphics.Color
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)

Просмотреть файл

@ -0,0 +1,18 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
)

Просмотреть файл

@ -0,0 +1,42 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
primary = Purple200,
primaryVariant = Purple700,
secondary = Teal200
)
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200
)
@Composable
fun CatalogTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}

Просмотреть файл

@ -0,0 +1,53 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.theme
import androidx.compose.material.Typography
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.microsoft.device.samples.dualscreenexperience.R
val Typography = Typography(
h5 = TextStyle(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
letterSpacing = 0.sp
),
h6 = TextStyle(
fontFamily = FontFamily(Font(R.font.roboto)),
fontWeight = FontWeight.Normal,
fontSize = 18.sp,
letterSpacing = 0.sp
),
body1 = TextStyle(
fontFamily = FontFamily(Font(R.font.dmsans_regular)),
fontWeight = FontWeight.Normal,
lineHeight = 24.sp,
fontSize = 16.sp,
letterSpacing = 0.sp
),
body2 = TextStyle(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Normal,
fontStyle = FontStyle.Italic,
fontSize = 16.sp,
lineHeight = 24.sp
),
caption = TextStyle(
fontFamily = FontFamily.Serif,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
color = Color.Gray
)
)

Просмотреть файл

@ -0,0 +1,160 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.min
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PagerState
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.ViewPager
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.rememberViewPagerState
import kotlinx.coroutines.launch
import kotlin.math.abs
@Composable
fun Catalog(
pane1WidthDp: Dp,
pane2WidthDp: Dp,
foldSizeDp: Dp,
isFeatureHorizontal: Boolean,
isSinglePortrait: Boolean,
showTwoPages: Boolean,
showSmallWindowWidthLayout: Boolean,
isFoldStateHalfOpened: Boolean,
catalogList: List<CatalogItem>
) {
val coroutineScope = rememberCoroutineScope()
// Calculate page text width based on the smallest pane width
val pageTextWidth = min(pane1WidthDp, pane2WidthDp)
// Calculate the necessary page padding, based on pane width differences and fold width
val pagePadding = abs(pane1WidthDp.value - pane2WidthDp.value).dp + foldSizeDp
val pagerState = rememberViewPagerState()
val pages = setupPages(
pageTextWidth,
pagePadding,
catalogList,
isFeatureHorizontal,
isSinglePortrait,
showTwoPages,
showSmallWindowWidthLayout,
isFoldStateHalfOpened
) { destinationPage ->
coroutineScope.launch {
pagerState.selectPageNumber(destinationPage)
}
}
PageViews(pagerState, pages, showTwoPages)
}
@Composable
fun PageViews(
pagerState: PagerState = rememberViewPagerState(),
pages: List<@Composable () -> Unit>,
showTwoPages: Boolean
) {
val maxPage = (pages.size - 1).coerceAtLeast(0)
pagerState.isDualMode = showTwoPages
pagerState.maxPage = maxPage
ViewPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) {
pages[page]()
}
}
private fun setupPages(
pageTextWidth: Dp,
pagePadding: Dp = 0.dp,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean,
isSinglePortrait: Boolean,
showTwoPages: Boolean,
showSmallWindowWidthLayout: Boolean,
isFoldStateHalfOpened: Boolean,
onItemClick: (Int) -> Unit
): List<@Composable () -> Unit> {
val modifier = if (pageTextWidth.value != 0f && showTwoPages) Modifier
.padding(end = pagePadding)
.width(pageTextWidth)
.fillMaxHeight()
.clipToBounds() else Modifier.fillMaxSize()
return listOf<@Composable () -> Unit>(
{
CatalogFirstPage(modifier = modifier, catalogList = catalogList, onItemClick)
},
{
CatalogSecondPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal,
showTwoPages = showTwoPages,
isSmallWindowWidth = showSmallWindowWidthLayout
)
},
{
CatalogThirdPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal,
isSinglePortrait = isSinglePortrait,
showTwoPages = showTwoPages,
isSmallWindowWidth = showSmallWindowWidthLayout,
isFoldStateHalfOpened = isFoldStateHalfOpened
)
},
{
CatalogFourthPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal,
showTwoPages = showTwoPages
)
},
{
CatalogFifthPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal,
isSmallWindowWidth = showSmallWindowWidthLayout,
showTwoPages = showTwoPages
)
},
{
CatalogSixthPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal
)
},
{
CatalogSeventhPage(
modifier = modifier,
catalogList = catalogList,
isFeatureHorizontal = isFeatureHorizontal,
isSmallWindowWidth = showSmallWindowWidthLayout,
showTwoPages = showTwoPages
)
},
)
}

Просмотреть файл

@ -0,0 +1,229 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val FIFTH_PAGE_FIRST_TEXT_ID = "fifthPageFirstText"
const val FIFTH_PAGE_FIRST_IMAGE_ID = "fifthPageFirstImage"
const val FIFTH_PAGE_SECOND_IMAGE_ID = "fifthPageSecondImage"
const val FIFTH_PAGE_SECOND_TEXT_ID = "fifthPageSecondText"
@Composable
fun CatalogFifthPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
isSmallWindowWidth: Boolean = false,
showTwoPages: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page5.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
val fourthPageConstraintSet = getConstraintSetForFifthPage(isFeatureHorizontal)
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
FifthPageContent(
modifier = modifier
.padding(
start = dimensionResource(
id = if (isSmallWindowWidth && !showTwoPages)
R.dimen.catalog_margin_small
else
R.dimen.catalog_horizontal_margin
),
end = dimensionResource(
id = if (isSmallWindowWidth && !showTwoPages)
R.dimen.catalog_margin_small
else
R.dimen.catalog_horizontal_margin
),
top = if (isFeatureHorizontal)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding),
)
.verticalScroll(rememberScrollState()),
constraintSet = fourthPageConstraintSet,
catalogItem = catalogItem,
isFeatureHorizontal = isFeatureHorizontal,
isSmallWindowWidth = isSmallWindowWidth,
showTwoPages = showTwoPages
)
}
}
private fun getConstraintSetForFifthPage(
isFeatureHorizontal: Boolean
) =
ConstraintSet {
val firstTextRef = createRefFor(FIFTH_PAGE_FIRST_TEXT_ID)
val firstImageRef = createRefFor(FIFTH_PAGE_FIRST_IMAGE_ID)
val secondImageRef = createRefFor(FIFTH_PAGE_SECOND_IMAGE_ID)
val secondTextRef = createRefFor(FIFTH_PAGE_SECOND_TEXT_ID)
val verticalGuideline = createGuidelineFromStart(0.5f)
val horizontalGuideline = createGuidelineFromTop(0.5f)
val topMargin = 20.dp
val verticalMargin = 16.dp
constrain(firstTextRef) {
start.linkTo(parent.start)
top.linkTo(parent.top, topMargin)
}
constrain(firstImageRef) {
start.linkTo(parent.start)
end.linkTo(verticalGuideline)
if (isFeatureHorizontal) {
bottom.linkTo(horizontalGuideline)
top.linkTo(firstTextRef.bottom)
} else {
top.linkTo(firstTextRef.bottom, topMargin)
}
height = Dimension.preferredWrapContent
width = Dimension.preferredWrapContent
}
constrain(secondImageRef) {
if (isFeatureHorizontal) {
top.linkTo(firstTextRef.bottom)
bottom.linkTo(horizontalGuideline, verticalMargin)
} else {
top.linkTo(firstImageRef.top)
}
start.linkTo(verticalGuideline, 8.dp)
end.linkTo(parent.end)
height = Dimension.fillToConstraints
}
constrain(secondTextRef) {
if (isFeatureHorizontal) {
top.linkTo(horizontalGuideline, margin = verticalMargin)
linkTo(start = parent.start, end = parent.end)
} else {
linkTo(start = firstTextRef.start, end = secondImageRef.start)
top.linkTo(firstImageRef.bottom, margin = verticalMargin)
}
width = Dimension.fillToConstraints
}
}
@Composable
fun FifthPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
isSmallWindowWidth: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
TextDescription(
modifier = Modifier.layoutId(FIFTH_PAGE_FIRST_TEXT_ID),
text = catalogItem.primaryDescription,
contentDescription = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
RoundedImage(
modifier = Modifier
.layoutId(FIFTH_PAGE_FIRST_IMAGE_ID)
.requiredWidth(
if (!showTwoPages && isSmallWindowWidth)
dimensionResource(id = R.dimen.catalog_small_screen_min_image_width)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.requiredHeight(
if (!showTwoPages && isSmallWindowWidth)
dimensionResource(id = R.dimen.catalog_base_min_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription,
contentScale = ContentScale.FillBounds
)
RoundedImage(
modifier = Modifier
.layoutId(FIFTH_PAGE_SECOND_IMAGE_ID)
.padding(
bottom = if (!showTwoPages && isSmallWindowWidth)
dimensionResource(id = R.dimen.small_margin)
else
dimensionResource(id = R.dimen.zero_padding)
)
.requiredWidth(
if (!showTwoPages && isSmallWindowWidth)
dimensionResource(id = R.dimen.catalog_small_screen_min_image_width)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.heightIn(
min = dimensionResource(id = R.dimen.catalog_medium_image_height),
max = dimensionResource(id = R.dimen.catalog_double_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondaryDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier
.padding(
end = if (!showTwoPages && isSmallWindowWidth)
dimensionResource(id = R.dimen.small_margin)
else
dimensionResource(id = R.dimen.catalog_horizontal_margin)
)
.layoutId(FIFTH_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
}
}

Просмотреть файл

@ -0,0 +1,187 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.ContentTextItem
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.contentDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val FIRST_PAGE_FIRST_TEXT_ID = "firstText"
const val FIRST_PAGE_SECOND_TEXT_ID = "secondText"
const val FIRST_PAGE_THIRD_TEXT_ID = "thirdText"
const val FIRST_PAGE_FOURTH_TEXT_ID = "fourthText"
const val FIRST_PAGE_FIFTH_TEXT_ID = "fifthText"
@Composable
fun CatalogFirstPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
onItemClick: (Int) -> Unit
) {
val pageNumberOrdinal = CatalogPage.Page1.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
TableOfContents(
modifier = Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState()),
catalogItem = catalogItem,
onItemClick = onItemClick
)
}
}
@Composable
private fun getConstraintSetForTableOfContents() = ConstraintSet {
val firstTextRef = createRefFor(FIRST_PAGE_FIRST_TEXT_ID)
val secondTextRef = createRefFor(FIRST_PAGE_SECOND_TEXT_ID)
val thirdTextRef = createRefFor(FIRST_PAGE_THIRD_TEXT_ID)
val fourthTextRef = createRefFor(FIRST_PAGE_FOURTH_TEXT_ID)
val fifthTextRef = createRefFor(FIRST_PAGE_FIFTH_TEXT_ID)
constrain(firstTextRef) {
start.linkTo(parent.start)
top.linkTo(parent.top)
}
constrain(secondTextRef) {
end.linkTo(parent.end)
start.linkTo(parent.start)
top.linkTo(firstTextRef.bottom)
}
constrain(thirdTextRef) {
end.linkTo(parent.end)
start.linkTo(parent.start)
top.linkTo(secondTextRef.bottom)
}
constrain(fourthTextRef) {
end.linkTo(parent.end)
start.linkTo(parent.start)
top.linkTo(thirdTextRef.bottom)
}
constrain(fifthTextRef) {
end.linkTo(parent.end)
start.linkTo(parent.start)
top.linkTo(fourthTextRef.bottom)
}
}
@Composable
fun TableOfContents(
modifier: Modifier = Modifier,
catalogItem: CatalogItem,
onItemClick: (Int) -> Unit
) {
val constraintSet = getConstraintSetForTableOfContents()
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier.fillMaxSize()
) {
Text(
modifier = Modifier
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
top = dimensionResource(id = R.dimen.catalog_margin_very_large),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin)
)
.layoutId(FIRST_PAGE_FIRST_TEXT_ID),
text = catalogItem.primaryDescription,
style = MaterialTheme.typography.h6.copy(color = MaterialTheme.colors.onSurface)
)
ContentTextItem(
modifier = Modifier
.contentDescription(
getPageContentDescription(
catalogItem.secondaryDescription ?: "",
stringResource(id = R.string.catalog_toc_item_content_description)
)
)
.layoutId(FIRST_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
destinationPage = CatalogPage.Page2.ordinal + 1,
onItemClick = onItemClick
)
ContentTextItem(
modifier = Modifier
.contentDescription(
getPageContentDescription(
catalogItem.thirdDescription ?: "",
stringResource(id = R.string.catalog_toc_item_content_description)
)
)
.layoutId(FIRST_PAGE_THIRD_TEXT_ID),
text = catalogItem.thirdDescription ?: "",
destinationPage = CatalogPage.Page3.ordinal + 1,
onItemClick = onItemClick
)
ContentTextItem(
modifier = Modifier
.contentDescription(
getPageContentDescription(
catalogItem.fourthDescription ?: "",
stringResource(id = R.string.catalog_toc_item_content_description)
)
)
.layoutId(FIRST_PAGE_FOURTH_TEXT_ID),
text = catalogItem.fourthDescription ?: "",
destinationPage = CatalogPage.Page4.ordinal + 1,
onItemClick = onItemClick
)
ContentTextItem(
modifier = Modifier
.contentDescription(
getPageContentDescription(
catalogItem.fifthDescription ?: "",
stringResource(id = R.string.catalog_toc_item_content_description)
)
)
.layoutId(FIRST_PAGE_FIFTH_TEXT_ID),
text = catalogItem.fifthDescription ?: "",
destinationPage = CatalogPage.Page6.ordinal + 1,
onItemClick = onItemClick
)
}
}
fun getPageContentDescription(value: String, replacement: String): String {
val titleIndex = value.indexOf('.')
val pageNumberIndex = value.lastIndexOf('.') + 1
return value.replaceRange(titleIndex, pageNumberIndex, replacement)
}

Просмотреть файл

@ -0,0 +1,232 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.constraintlayout.compose.ChainStyle
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val FOURTH_PAGE_FIRST_IMAGE_ID = "fourthPageFirstImage"
const val FOURTH_PAGE_SECOND_IMAGE_ID = "fourthPageSecondImage"
const val FOURTH_PAGE_FIRST_TEXT_ID = "fourthPageFirstText"
const val FOURTH_PAGE_SECOND_TEXT_ID = "fourthPageSecondText"
const val FOURTH_PAGE_THIRD_TEXT_ID = "fourthPageThirdText"
@Composable
fun CatalogFourthPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
showTwoPages: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page4.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
val fourthPageConstraintSet = getConstraintSetForFourthPage(isFeatureHorizontal)
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
FourthPageContent(
modifier
.verticalScroll(rememberScrollState())
.padding(
start = if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_page_padding)
else
dimensionResource(id = R.dimen.zero_padding),
top = if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding)
),
fourthPageConstraintSet,
catalogItem,
isFeatureHorizontal,
showTwoPages
)
}
}
private fun getConstraintSetForFourthPage(isFeatureHorizontal: Boolean) = ConstraintSet {
val firstImageRef = createRefFor(FOURTH_PAGE_FIRST_IMAGE_ID)
val secondImageRef = createRefFor(FOURTH_PAGE_SECOND_IMAGE_ID)
val firstTextRef = createRefFor(FOURTH_PAGE_FIRST_TEXT_ID)
val secondTextRef = createRefFor(FOURTH_PAGE_SECOND_TEXT_ID)
val thirdTextRef = createRefFor(FOURTH_PAGE_THIRD_TEXT_ID)
val horizontalGuideline = createGuidelineFromTop(0.5f)
constrain(firstTextRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(firstImageRef.bottom)
if (isFeatureHorizontal) {
bottom.linkTo(horizontalGuideline)
}
}
constrain(secondTextRef) {
top.linkTo(if (isFeatureHorizontal) horizontalGuideline else firstTextRef.bottom)
start.linkTo(firstTextRef.start)
}
constrain(thirdTextRef) {
linkTo(start = firstTextRef.start, end = firstTextRef.end)
top.linkTo(secondTextRef.bottom)
}
constrain(firstImageRef) {
top.linkTo(parent.top)
start.linkTo(secondTextRef.start)
}
constrain(secondImageRef) {
start.linkTo(firstImageRef.start)
top.linkTo(firstImageRef.top)
end.linkTo(secondTextRef.end)
}
createHorizontalChain(
firstImageRef,
secondImageRef,
chainStyle = ChainStyle.Spread
)
}
@Composable
fun FourthPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
RoundedImage(
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.catalog_top_margin))
.layoutId(FOURTH_PAGE_FIRST_IMAGE_ID)
.requiredWidth(
if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_max_image_height_dual_landscape)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
)
.requiredHeight(
if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_max_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription
)
RoundedImage(
modifier = Modifier
.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin),
end = if (showTwoPages)
dimensionResource(id = R.dimen.catalog_margin_large)
else
dimensionResource(id = R.dimen.zero_padding)
)
.layoutId(FOURTH_PAGE_SECOND_IMAGE_ID)
.requiredWidth(
if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_max_image_height_dual_landscape)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
)
.requiredHeight(
if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.catalog_max_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondPictureDescription
)
TextDescription(
modifier = Modifier
.padding(
horizontal = if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.zero_padding)
else
dimensionResource(id = R.dimen.medium_margin)
)
.layoutId(FOURTH_PAGE_FIRST_TEXT_ID),
text = catalogItem.primaryDescription,
contentDescription = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
TextDescription(
modifier = Modifier
.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin),
start = if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.zero_padding)
else
dimensionResource(id = R.dimen.medium_margin)
)
.layoutId(FOURTH_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_20)
else
fontDimensionResource(id = R.dimen.text_size_16)
)
TextDescription(
modifier = Modifier
.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin),
start = if (isFeatureHorizontal && showTwoPages)
dimensionResource(id = R.dimen.zero_padding)
else
dimensionResource(id = R.dimen.medium_margin)
)
.layoutId(FOURTH_PAGE_THIRD_TEXT_ID),
text = catalogItem.thirdDescription ?: "",
contentDescription = catalogItem.thirdDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
}
}

Просмотреть файл

@ -0,0 +1,332 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ChainStyle
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val SECOND_PAGE_TITLE_TEXT_ID = "secondPageTitle"
const val SECOND_PAGE_FIRST_ROW_ID = "secondPageFirstRow"
const val SECOND_PAGE_SECOND_TEXT_ID = "secondPageSecondText"
const val SECOND_PAGE_THIRD_TEXT_ID = "secondPageThirdText"
const val SECOND_PAGE_FIRST_IMAGE_ID = "secondPageFirstImage"
const val SECOND_PAGE_FIRST_ROW_IMAGE_ID = "secondPageFirstRowImage"
const val SECOND_PAGE_SECOND_ROW_IMAGE_ID = "secondPageSecondRowImage"
const val HORIZONTAL_GUIDELINE_FRACTION = 0.5f
@Composable
fun CatalogSecondPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
showTwoPages: Boolean = false,
isSmallWindowWidth: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page2.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
val secondPageConstraintSet = if (isSmallWindowWidth)
getConstraintSetForSmallWindowWidth()
else
getConstraintSetForSecondPage(isFeatureHorizontal)
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
SecondPageContent(
modifier = modifier
.verticalScroll(rememberScrollState())
.padding(
top = if (isFeatureHorizontal)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding)
),
constraintSet = secondPageConstraintSet,
catalogItem = catalogItem,
isFeatureHorizontal = isFeatureHorizontal,
isSmallWindowWidth = isSmallWindowWidth,
showTwoPages = showTwoPages
)
}
}
@Composable
private fun getConstraintSetForSecondPage(isFeatureHorizontal: Boolean) = ConstraintSet {
val titleRef = createRefFor(SECOND_PAGE_TITLE_TEXT_ID)
val firstRowRef = createRefFor(SECOND_PAGE_FIRST_ROW_ID)
val thirdTextRef = createRefFor(SECOND_PAGE_THIRD_TEXT_ID)
val firstRowImageRef = createRefFor(SECOND_PAGE_FIRST_ROW_IMAGE_ID)
val secondRowImageRef = createRefFor(SECOND_PAGE_SECOND_ROW_IMAGE_ID)
val horizontalGuideline = createGuidelineFromTop(HORIZONTAL_GUIDELINE_FRACTION)
val horizontalMargin = 36.dp
val topMargin = 20.dp
val smallMargin = 8.dp
constrain(titleRef) {
top.linkTo(parent.top, topMargin)
start.linkTo(parent.start, horizontalMargin)
}
constrain(firstRowRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(titleRef.bottom, smallMargin)
if (isFeatureHorizontal) {
bottom.linkTo(horizontalGuideline)
}
}
constrain(thirdTextRef) {
linkTo(start = parent.start, end = parent.end)
if (isFeatureHorizontal) {
top.linkTo(horizontalGuideline)
} else {
top.linkTo(firstRowRef.bottom)
}
}
constrain(firstRowImageRef) {
top.linkTo(thirdTextRef.bottom, topMargin)
start.linkTo(firstRowRef.start)
}
constrain(secondRowImageRef) {
start.linkTo(firstRowImageRef.start)
top.linkTo(firstRowImageRef.top)
end.linkTo(firstRowRef.end)
}
createHorizontalChain(
firstRowImageRef,
secondRowImageRef,
chainStyle = ChainStyle.SpreadInside
)
}
@Composable
private fun getConstraintSetForSmallWindowWidth() = ConstraintSet {
val titleRef = createRefFor(SECOND_PAGE_TITLE_TEXT_ID)
val firstImageRef = createRefFor(SECOND_PAGE_FIRST_IMAGE_ID)
val secondTextRef = createRefFor(SECOND_PAGE_SECOND_TEXT_ID)
val thirdTextRef = createRefFor(SECOND_PAGE_THIRD_TEXT_ID)
val firstRowImageRef = createRefFor(SECOND_PAGE_FIRST_ROW_IMAGE_ID)
val secondRowImageRef = createRefFor(SECOND_PAGE_SECOND_ROW_IMAGE_ID)
val horizontalMargin = 36.dp
val topMargin = 20.dp
val smallMargin = 8.dp
constrain(titleRef) {
top.linkTo(parent.top, topMargin)
start.linkTo(parent.start, horizontalMargin)
}
constrain(secondTextRef) {
linkTo(
start = parent.start,
end = parent.end,
startMargin = horizontalMargin,
endMargin = horizontalMargin
)
top.linkTo(titleRef.bottom, smallMargin)
}
constrain(firstImageRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(secondTextRef.bottom, smallMargin)
height = Dimension.fillToConstraints
}
constrain(thirdTextRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(firstImageRef.bottom)
}
constrain(firstRowImageRef) {
top.linkTo(thirdTextRef.bottom, smallMargin)
start.linkTo(secondTextRef.start, horizontalMargin)
}
constrain(secondRowImageRef) {
start.linkTo(firstRowImageRef.start)
top.linkTo(firstRowImageRef.top)
end.linkTo(secondTextRef.end, horizontalMargin)
}
createHorizontalChain(
firstRowImageRef,
secondRowImageRef,
chainStyle = ChainStyle.Spread
)
}
@Composable
private fun SecondPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
isSmallWindowWidth: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
TextDescription(
modifier = Modifier.layoutId(SECOND_PAGE_TITLE_TEXT_ID),
text = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_20)
else
fontDimensionResource(id = R.dimen.text_size_16),
contentDescription = catalogItem.primaryDescription
)
if (isSmallWindowWidth && !showTwoPages) {
TextDescription(
modifier = Modifier
.layoutId(SECOND_PAGE_SECOND_TEXT_ID)
.padding(horizontal = dimensionResource(id = R.dimen.catalog_horizontal_margin)),
text = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.secondaryDescription ?: ""
)
RoundedImage(
modifier = Modifier
.layoutId(SECOND_PAGE_FIRST_IMAGE_ID)
.requiredWidth(dimensionResource(id = R.dimen.catalog_small_screen_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription,
contentScale = ContentScale.FillHeight
)
} else {
Row(
modifier = Modifier
.layoutId(SECOND_PAGE_FIRST_ROW_ID)
.padding(top = dimensionResource(id = R.dimen.catalog_margin_small)),
horizontalArrangement = Arrangement.SpaceBetween
) {
RoundedImage(
modifier = Modifier
.padding(start = dimensionResource(id = R.dimen.catalog_image_margin_start))
.requiredWidth(dimensionResource(id = R.dimen.catalog_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription
)
TextDescription(
modifier = Modifier.padding(
top = dimensionResource(id = R.dimen.small_margin),
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin)
),
text = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.secondaryDescription ?: ""
)
}
}
TextDescription(
modifier = Modifier
.layoutId(SECOND_PAGE_THIRD_TEXT_ID)
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin),
top = dimensionResource(id = R.dimen.catalog_margin_large)
),
text = catalogItem.thirdDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.thirdDescription ?: ""
)
RoundedImage(
modifier = Modifier
.layoutId(SECOND_PAGE_FIRST_ROW_IMAGE_ID)
.padding(
start = if (isSmallWindowWidth && !showTwoPages)
dimensionResource(id = R.dimen.zero_padding)
else
dimensionResource(id = R.dimen.catalog_image_margin_start)
)
.requiredWidth(
if (isSmallWindowWidth && !showTwoPages)
dimensionResource(id = R.dimen.catalog_small_screen_min_image_width)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.firstPictureDescription
)
RoundedImage(
modifier = Modifier
.layoutId(SECOND_PAGE_SECOND_ROW_IMAGE_ID)
.padding(
end = if (isSmallWindowWidth && !showTwoPages)
dimensionResource(id = R.dimen.zero_padding)
else
dimensionResource(id = R.dimen.catalog_image_margin_end)
)
.requiredWidth(
if (isSmallWindowWidth && !showTwoPages)
dimensionResource(id = R.dimen.catalog_small_screen_min_image_width)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.thirdPicture)),
contentDescription = catalogItem.firstPictureDescription
)
}
}

Просмотреть файл

@ -0,0 +1,285 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val SEVENTH_PAGE_FIRST_ROW_ID = "seventhPageFirstRow"
const val SEVENTH_PAGE_SECOND_ROW_ID = "seventhPageSecondRow"
const val SEVENTH_PAGE_FIRST_IMAGE_ID = "seventhPageFirstImage"
const val SEVENTH_PAGE_SECOND_IMAGE_ID = "seventhPageSecondImage"
const val SEVENTH_PAGE_FIRST_TEXT_ID = "seventhPageFirstText"
const val SEVENTH_PAGE_SECOND_TEXT_ID = "seventhPageSecondText"
@Composable
fun CatalogSeventhPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
isSmallWindowWidth: Boolean = false,
showTwoPages: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page7.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
if (isSmallWindowWidth && !showTwoPages) {
SeventhPageContentSmallWidth(
modifier
.verticalScroll(rememberScrollState())
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin),
top = if (isFeatureHorizontal)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding)
),
getConstraintSetForSmallWindowWidth(),
catalogItem,
isFeatureHorizontal,
showTwoPages
)
} else {
SeventhPageContent(
modifier
.verticalScroll(rememberScrollState())
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin),
top = if (isFeatureHorizontal)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding)
),
getConstraintSetForSeventhPage(isFeatureHorizontal),
catalogItem,
isFeatureHorizontal,
showTwoPages
)
}
}
}
private fun getConstraintSetForSeventhPage(isFeatureHorizontal: Boolean) = ConstraintSet {
val firstRowRef = createRefFor(SEVENTH_PAGE_FIRST_ROW_ID)
val secondRowRef = createRefFor(SEVENTH_PAGE_SECOND_ROW_ID)
val horizontalGuideline = createGuidelineFromTop(0.5f)
val topMargin = 20.dp
constrain(firstRowRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(parent.top)
if (isFeatureHorizontal) {
bottom.linkTo(horizontalGuideline)
}
}
constrain(secondRowRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(if (isFeatureHorizontal) horizontalGuideline else firstRowRef.bottom, topMargin)
}
}
@Composable
fun SeventhPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
Row(
modifier = Modifier
.wrapContentHeight()
.padding(top = dimensionResource(id = R.dimen.catalog_top_margin))
.layoutId(SEVENTH_PAGE_FIRST_ROW_ID),
horizontalArrangement = Arrangement.SpaceBetween
) {
RoundedImage(
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.catalog_top_margin))
.weight(1f)
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription
)
TextDescription(
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.normal_margin))
.weight(1f),
text = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.primaryDescription
)
}
Row(
modifier = Modifier
.layoutId(SEVENTH_PAGE_SECOND_ROW_ID),
horizontalArrangement = Arrangement.SpaceBetween
) {
TextDescription(
modifier = Modifier.weight(1f),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
RoundedImage(
modifier = Modifier
.weight(1f)
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondaryDescription
)
}
}
}
@Composable
private fun getConstraintSetForSmallWindowWidth() = ConstraintSet {
val firstImageRef = createRefFor(SEVENTH_PAGE_FIRST_IMAGE_ID)
val firstTextRef = createRefFor(SEVENTH_PAGE_FIRST_TEXT_ID)
val secondImageRef = createRefFor(SEVENTH_PAGE_SECOND_IMAGE_ID)
val secondTextRef = createRefFor(SEVENTH_PAGE_SECOND_TEXT_ID)
val topMargin = 20.dp
constrain(firstImageRef) {
top.linkTo(parent.top, topMargin)
linkTo(start = parent.start, end = parent.end)
}
constrain(firstTextRef) {
top.linkTo(firstImageRef.bottom, topMargin)
linkTo(start = parent.start, end = parent.end)
}
constrain(secondImageRef) {
top.linkTo(firstTextRef.bottom, topMargin)
linkTo(start = parent.start, end = parent.end)
}
constrain(secondTextRef) {
top.linkTo(secondImageRef.bottom, topMargin)
linkTo(start = parent.start, end = parent.end)
}
}
@Composable
fun SeventhPageContentSmallWidth(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
RoundedImage(
modifier = Modifier
.layoutId(SEVENTH_PAGE_FIRST_IMAGE_ID)
.requiredWidth(dimensionResource(id = R.dimen.catalog_small_screen_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height))
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier.layoutId(SEVENTH_PAGE_FIRST_TEXT_ID),
text = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.primaryDescription
)
RoundedImage(
modifier = Modifier
.layoutId(SEVENTH_PAGE_SECOND_IMAGE_ID)
.requiredWidth(dimensionResource(id = R.dimen.catalog_small_screen_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height))
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondaryDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier.layoutId(SEVENTH_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
}
}

Просмотреть файл

@ -0,0 +1,150 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val SIXTH_PAGE_FIRST_TEXT_ID = "sixthPageFirstText"
const val SIXTH_PAGE_FIRST_IMAGE_ID = "sixthPageFirstImage"
const val SIXTH_PAGE_SECOND_TEXT_ID = "sixthPageSecondText"
@Composable
fun CatalogSixthPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
showTwoPages: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page6.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
val sixthPageConstraintSet = getConstraintSetForSixthPage(isFeatureHorizontal)
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
SixthPageContent(
modifier
.verticalScroll(rememberScrollState())
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin),
bottom = dimensionResource(id = R.dimen.catalog_margin_normal),
top = if (isFeatureHorizontal)
dimensionResource(id = R.dimen.catalog_margin_normal)
else
dimensionResource(id = R.dimen.zero_padding),
),
sixthPageConstraintSet,
catalogItem,
isFeatureHorizontal,
showTwoPages
)
}
}
private fun getConstraintSetForSixthPage(isFeatureHorizontal: Boolean) = ConstraintSet {
val firstTextRef = createRefFor(SIXTH_PAGE_FIRST_TEXT_ID)
val firstImageRef = createRefFor(SIXTH_PAGE_FIRST_IMAGE_ID)
val secondTextRef = createRefFor(SIXTH_PAGE_SECOND_TEXT_ID)
val horizontalGuideline = createGuidelineFromTop(0.5f)
val topMargin = 20.dp
constrain(firstTextRef) {
start.linkTo(parent.start)
top.linkTo(parent.top, topMargin)
}
constrain(firstImageRef) {
linkTo(start = parent.start, end = parent.end)
if (isFeatureHorizontal) {
linkTo(top = firstTextRef.bottom, bottom = horizontalGuideline, topMargin = topMargin)
} else {
top.linkTo(firstTextRef.bottom, margin = topMargin)
}
width = Dimension.wrapContent
}
constrain(secondTextRef) {
linkTo(start = parent.start, end = parent.end)
if (isFeatureHorizontal) {
top.linkTo(horizontalGuideline, margin = topMargin)
} else {
top.linkTo(firstImageRef.bottom, margin = topMargin)
}
}
}
@Composable
fun SixthPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
showTwoPages: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
TextDescription(
modifier = Modifier.layoutId(SIXTH_PAGE_FIRST_TEXT_ID),
text = catalogItem.primaryDescription,
contentDescription = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_20)
else
fontDimensionResource(id = R.dimen.text_size_16)
)
RoundedImage(
modifier = Modifier
.layoutId(SIXTH_PAGE_FIRST_IMAGE_ID)
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription
)
TextDescription(
modifier = Modifier.layoutId(SIXTH_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
}
}

Просмотреть файл

@ -0,0 +1,355 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.ui.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import coil.compose.rememberAsyncImagePainter
import com.microsoft.device.samples.dualscreenexperience.R
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem
import com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogPage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.PageLayout
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.RoundedImage
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.TextDescription
import com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils.fontDimensionResource
import com.microsoft.device.samples.dualscreenexperience.presentation.util.getImageUri
import com.microsoft.device.samples.dualscreenexperience.presentation.util.sizeOrZero
const val THIRD_PAGE_FIRST_ROW_ID = "thirdPageFirstRow"
const val THIRD_PAGE_SECOND_ROW_ID = "thirdPageSecondRow"
const val THIRD_PAGE_FIRST_IMAGE_ID = "thirdPageFirstImage"
const val THIRD_PAGE_FIRST_TEXT_ID = "thirdPageFirstText"
const val THIRD_PAGE_SECOND_TEXT_ID = "thirdPageSecondText"
const val THIRD_PAGE_THIRD_TEXT_ID = "thirdPageThirdText"
const val THIRD_PAGE_SECOND_IMAGE_ID = "thirdPageSecondImage"
const val ROW_WEIGHT = 1f
@Composable
fun CatalogThirdPage(
modifier: Modifier = Modifier,
catalogList: List<CatalogItem>,
isFeatureHorizontal: Boolean = false,
isSinglePortrait: Boolean,
showTwoPages: Boolean = false,
isSmallWindowWidth: Boolean = false,
isFoldStateHalfOpened: Boolean = false
) {
val pageNumberOrdinal = CatalogPage.Page3.ordinal
val catalogItem = catalogList[pageNumberOrdinal]
PageLayout(
modifier,
pageNumberOrdinal + 1,
catalogList.sizeOrZero()
) {
val contentModifier =
modifier
.verticalScroll(rememberScrollState())
.padding(top = dimensionResource(id = R.dimen.catalog_margin_normal))
if (isSmallWindowWidth) {
ThirdPageSmallWindowWidthContent(
modifier = contentModifier,
constraintSet = getConstraintSetForSmallWindowWidth(),
catalogItem = catalogItem
)
} else {
ThirdPageContent(
modifier = contentModifier,
constraintSet = getConstraintSetForThirdPage(isFeatureHorizontal),
catalogItem = catalogItem,
isFeatureHorizontal = isFeatureHorizontal,
isSinglePortrait = isSinglePortrait,
showTwoPages = showTwoPages,
isFoldStateHalfOpened = isFoldStateHalfOpened
)
}
}
}
private fun getConstraintSetForThirdPage(isFeatureHorizontal: Boolean) = ConstraintSet {
val firstRowRef = createRefFor(THIRD_PAGE_FIRST_ROW_ID)
val secondRowRef = createRefFor(THIRD_PAGE_SECOND_ROW_ID)
val horizontalGuideline = createGuidelineFromTop(0.5f)
val topMargin = 20.dp
val smallMargin = 8.dp
constrain(firstRowRef) {
linkTo(
start = parent.start,
startMargin = smallMargin,
end = parent.end,
endMargin = smallMargin
)
top.linkTo(parent.top)
if (isFeatureHorizontal) {
bottom.linkTo(horizontalGuideline)
}
}
constrain(secondRowRef) {
linkTo(start = parent.start, end = parent.end)
top.linkTo(if (isFeatureHorizontal) horizontalGuideline else firstRowRef.bottom, topMargin)
}
}
@Composable
private fun getConstraintSetForSmallWindowWidth() = ConstraintSet {
val firstImageRef = createRefFor(THIRD_PAGE_FIRST_IMAGE_ID)
val firstTextRef = createRefFor(THIRD_PAGE_FIRST_TEXT_ID)
val secondImageRef = createRefFor(THIRD_PAGE_SECOND_IMAGE_ID)
val secondTextRef = createRefFor(THIRD_PAGE_SECOND_TEXT_ID)
val thirdTextRef = createRefFor(THIRD_PAGE_THIRD_TEXT_ID)
val horizontalMargin = 36.dp
val topMargin = 20.dp
val smallMargin = 8.dp
constrain(firstImageRef) {
top.linkTo(parent.top, topMargin)
linkTo(start = parent.start, end = parent.end)
}
constrain(firstTextRef) {
top.linkTo(firstImageRef.bottom, topMargin)
linkTo(
start = parent.start,
end = parent.end,
startMargin = horizontalMargin,
endMargin = horizontalMargin
)
}
constrain(secondImageRef) {
top.linkTo(firstTextRef.bottom, smallMargin)
linkTo(start = parent.start, end = parent.end)
}
constrain(secondTextRef) {
top.linkTo(secondImageRef.bottom, topMargin)
start.linkTo(parent.start, horizontalMargin)
}
constrain(thirdTextRef) {
top.linkTo(secondTextRef.bottom)
linkTo(
start = parent.start,
end = parent.end,
startMargin = horizontalMargin,
endMargin = horizontalMargin
)
}
}
@Composable
fun ThirdPageContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem,
isFeatureHorizontal: Boolean,
isSinglePortrait: Boolean,
showTwoPages: Boolean,
isFoldStateHalfOpened: Boolean
) {
ConstraintLayout(
constraintSet = constraintSet,
modifier = modifier
) {
Row(
modifier = Modifier
.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin),
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.normal_margin)
)
.layoutId(THIRD_PAGE_FIRST_ROW_ID),
horizontalArrangement = Arrangement.SpaceBetween
) {
RoundedImage(
modifier = Modifier
.weight(ROW_WEIGHT)
.requiredWidth(
if ((isFeatureHorizontal && !showTwoPages) ||
isSinglePortrait || (isFoldStateHalfOpened && !showTwoPages)
)
dimensionResource(id = R.dimen.catalog_max_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.requiredHeight(
if ((isFeatureHorizontal && !showTwoPages) ||
isSinglePortrait || (isFoldStateHalfOpened && !showTwoPages)
)
dimensionResource(id = R.dimen.catalog_medium_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.normal_margin)
)
.weight(ROW_WEIGHT),
text = catalogItem.primaryDescription,
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.primaryDescription
)
}
Row(
modifier = Modifier
.padding(end = dimensionResource(id = R.dimen.normal_margin))
.layoutId(THIRD_PAGE_SECOND_ROW_ID),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
modifier = Modifier
.padding(horizontal = dimensionResource(id = R.dimen.catalog_horizontal_margin))
.weight(ROW_WEIGHT)
) {
TextDescription(
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_20)
else
fontDimensionResource(id = R.dimen.text_size_16)
)
TextDescription(
modifier = Modifier.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin)
),
text = catalogItem.thirdDescription ?: "",
contentDescription = catalogItem.thirdDescription ?: "",
fontSize = if (isFeatureHorizontal && showTwoPages)
fontDimensionResource(id = R.dimen.text_size_16)
else
fontDimensionResource(id = R.dimen.text_size_12)
)
}
RoundedImage(
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.catalog_top_margin))
.weight(ROW_WEIGHT)
.requiredWidth(
if ((isFeatureHorizontal && !showTwoPages) ||
isSinglePortrait || (isFoldStateHalfOpened && !showTwoPages)
)
dimensionResource(id = R.dimen.catalog_max_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_width)
)
.requiredHeight(
if ((isFeatureHorizontal && !showTwoPages) ||
isSinglePortrait || (isFoldStateHalfOpened && !showTwoPages)
)
dimensionResource(id = R.dimen.catalog_medium_image_height)
else
dimensionResource(id = R.dimen.catalog_min_image_height)
)
.heightIn(
min = dimensionResource(id = R.dimen.catalog_min_image_height),
max = dimensionResource(id = R.dimen.catalog_max_image_height)
),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondaryDescription,
contentScale = ContentScale.FillBounds
)
}
}
}
@Composable
fun ThirdPageSmallWindowWidthContent(
modifier: Modifier,
constraintSet: ConstraintSet,
catalogItem: CatalogItem
) {
ConstraintLayout(constraintSet, modifier = modifier) {
RoundedImage(
modifier = Modifier
.layoutId(THIRD_PAGE_FIRST_IMAGE_ID)
.requiredWidth(dimensionResource(id = R.dimen.catalog_small_screen_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.firstPicture)),
contentDescription = catalogItem.firstPictureDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier
.layoutId(THIRD_PAGE_FIRST_TEXT_ID)
.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.normal_margin)
),
text = catalogItem.primaryDescription,
fontSize =
fontDimensionResource(id = R.dimen.text_size_12),
contentDescription = catalogItem.primaryDescription
)
RoundedImage(
modifier = Modifier
.layoutId(THIRD_PAGE_SECOND_IMAGE_ID)
.padding(top = dimensionResource(id = R.dimen.catalog_top_margin))
.requiredWidth(dimensionResource(id = R.dimen.catalog_small_screen_min_image_width))
.requiredHeight(dimensionResource(id = R.dimen.catalog_min_image_height)),
painter = rememberAsyncImagePainter(model = getImageUri(catalogItem.secondPicture)),
contentDescription = catalogItem.secondaryDescription,
contentScale = ContentScale.FillBounds
)
TextDescription(
modifier = Modifier.layoutId(THIRD_PAGE_SECOND_TEXT_ID),
text = catalogItem.secondaryDescription ?: "",
contentDescription = catalogItem.secondaryDescription ?: "",
fontSize = fontDimensionResource(id = R.dimen.text_size_16)
)
TextDescription(
modifier = Modifier
.layoutId(THIRD_PAGE_THIRD_TEXT_ID)
.padding(
top = dimensionResource(id = R.dimen.catalog_top_margin),
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.normal_margin)
),
text = catalogItem.thirdDescription ?: "",
contentDescription = catalogItem.thirdDescription ?: "",
fontSize = fontDimensionResource(id = R.dimen.text_size_12)
)
}
}

Просмотреть файл

@ -0,0 +1,17 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
fun Modifier.contentDescription(contentDescription: String) =
this.semantics {
this.contentDescription = contentDescription
}

Просмотреть файл

@ -0,0 +1,19 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils
import androidx.annotation.DimenRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.sp
@Composable
@ReadOnlyComposable
fun fontDimensionResource(@DimenRes id: Int) =
dimensionResource(id = id).value.sp

Просмотреть файл

@ -0,0 +1,177 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import com.microsoft.device.samples.dualscreenexperience.R
const val CONTENT_ID = "tableOfContents"
const val BOTTOM_PAGE_NUMBER_ID = "bottomPageNumber"
@Composable
fun PageLayout(
modifier: Modifier = Modifier,
pageNumber: Int,
maxPageNumber: Int,
content: @Composable () -> Unit
) {
val constraintSet = getMainConstraintSet()
ConstraintLayout(constraintSet = constraintSet, modifier = modifier) {
Box(
modifier = Modifier
.layoutId(CONTENT_ID)
.fillMaxHeight()
.padding(bottom = dimensionResource(id = R.dimen.catalog_margin_normal))
) {
content()
}
BottomPageNumber(
modifier = Modifier.layoutId(BOTTOM_PAGE_NUMBER_ID),
text = stringResource(
id = R.string.catalog_page_no, pageNumber, maxPageNumber
)
)
}
}
@Composable
private fun getMainConstraintSet() = ConstraintSet {
val contentRef = createRefFor(CONTENT_ID)
val bottomPageNumberRef = createRefFor(BOTTOM_PAGE_NUMBER_ID)
constrain(contentRef) {
linkTo(start = parent.start, end = parent.end)
linkTo(top = parent.top, bottom = bottomPageNumberRef.top)
}
constrain(bottomPageNumberRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}
}
@Composable
fun BottomPageNumber(modifier: Modifier = Modifier, text: String) {
Text(
text = text,
style = TextStyle(
color = MaterialTheme.colors.onSurface,
fontFamily = FontFamily(Font(R.font.dmsans_regular)),
lineHeight = fontDimensionResource(id = R.dimen.text_size_4),
fontSize = fontDimensionResource(id = R.dimen.text_size_12),
textAlign = TextAlign.Start
),
modifier = modifier.padding(
start = dimensionResource(id = R.dimen.catalog_horizontal_margin),
end = dimensionResource(id = R.dimen.catalog_horizontal_margin),
bottom = dimensionResource(id = R.dimen.catalog_margin_small)
)
)
}
@Composable
fun TextDescription(
modifier: Modifier = Modifier,
text: String,
contentDescription: String,
textStyle: TextStyle = MaterialTheme.typography.body1,
color: Color = MaterialTheme.colors.onSurface,
textAlign: TextAlign = TextAlign.Start,
fontSize: TextUnit = fontDimensionResource(id = R.dimen.text_size_12),
lineHeight: TextUnit = fontDimensionResource(id = R.dimen.text_size_20)
) {
Text(
modifier = modifier.contentDescription(contentDescription),
text = text,
lineHeight = lineHeight,
textAlign = textAlign,
style = textStyle,
color = color,
fontSize = fontSize
)
}
@Composable
fun RoundedImage(
modifier: Modifier = Modifier,
painter: Painter,
contentDescription: String?,
contentScale: ContentScale = ContentScale.None
) {
Image(
modifier = modifier
.clip(MaterialTheme.shapes.small)
.clipToBounds(),
painter = painter,
contentDescription = contentDescription,
contentScale = contentScale
)
}
@Composable
fun ContentTextItem(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.onSurface,
textAlign: TextAlign = TextAlign.Start,
text: String,
destinationPage: Int,
onItemClick: (Int) -> Unit,
) {
Row(
modifier = modifier
.padding(
vertical = dimensionResource(id = R.dimen.catalog_margin_normal),
horizontal = dimensionResource(id = R.dimen.catalog_horizontal_margin)
)
.wrapContentWidth()
.clickable { onItemClick(destinationPage - 1) },
) {
Text(
modifier = Modifier.weight(1f),
text = text,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body1,
color = color,
textAlign = textAlign
)
Text(
text = destinationPage.toString(),
style = MaterialTheme.typography.body1,
color = color
)
}
}

Просмотреть файл

@ -0,0 +1,261 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.catalog.utils
import androidx.annotation.IntRange
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.mapSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.ParentDataModifier
import androidx.compose.ui.unit.Density
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
/**
* This is a modified version of the "Pager" from the Compose JetCaster official sample under Apache License v2
* Source code:
* https://github.com/android/compose-samples/blob/main/Jetcaster/app/src/main/java/com/example/jetcaster/util/Pager.kt
* License:
* https://www.apache.org/licenses/LICENSE-2.0
*/
class PagerState(
currentPage: Int = 0,
minPage: Int = 0,
maxPage: Int = 0
) {
private var _minPage by mutableStateOf(minPage)
var minPage: Int
get() = _minPage
set(value) {
_minPage = value.coerceAtMost(_maxPage)
_currentPage = _currentPage.coerceIn(_minPage, _maxPage)
}
private var _maxPage by mutableStateOf(maxPage, structuralEqualityPolicy())
var maxPage: Int
get() = _maxPage
set(value) {
_maxPage = value.coerceAtLeast(_minPage)
_currentPage = if (isDualMode && _currentPage == _maxPage) {
_currentPage.coerceIn(_minPage - 1, _maxPage - 1)
} else {
_currentPage.coerceIn(_minPage, _maxPage)
}
}
private var _currentPage by mutableStateOf(currentPage.coerceIn(minPage, maxPage))
var currentPage: Int
get() = _currentPage
set(value) {
_currentPage = value.coerceIn(minPage, maxPage)
}
enum class SelectionState { Selected, Undecided }
var selectionState by mutableStateOf(SelectionState.Selected)
var isDualMode: Boolean = false // support dual-screen mode
suspend fun selectPage() {
currentPage -= updatePage(currentPageOffset)
snapToOffset(0f)
selectionState = SelectionState.Selected
}
suspend fun selectPageNumber(pageNumber: Int) {
currentPage = pageNumber
snapToOffset(0f)
}
private var _currentPageOffset = Animatable(0f).apply {
updateBounds(-1f, 1f)
}
val currentPageOffset: Float
get() = _currentPageOffset.value
suspend fun snapToOffset(offset: Float) {
val max = if (currentPage == minPage) 0f else 1f
val lastPage = if (isDualMode) maxPage - 1 else maxPage
val min = if (currentPage == lastPage) 0f else -1f
_currentPageOffset.snapTo(offset.coerceIn(min, max))
}
private val minDragOffset = 0.1f // customize the minimum offset to enhance the dragging gesture
private fun roundOffset(original: Float): Float {
return if (original > minDragOffset) 1f else if (original < -minDragOffset) -1f else 0f // ease the scrolling transition
}
private fun updatePage(offset: Float): Int {
return if (offset > minDragOffset) 1 else if (offset < -minDragOffset) -1 else 0
}
suspend fun fling(velocity: Float) {
if (velocity < 0 && currentPage == maxPage) return
if (velocity > 0 && currentPage == minPage) return
val offset = roundOffset(currentPageOffset)
_currentPageOffset.animateTo(offset)
selectPage()
}
override fun toString(): String = "PagerState{minPage=$minPage, maxPage=$maxPage, " +
"currentPage=$currentPage, currentPageOffset=$currentPageOffset}"
companion object {
/**
* Default [Saver] implementation for [PagerState]
*/
val Saver = run {
val currentPageKey = "currentPage"
val minPageKey = "minPage"
val maxPageKey = "maxPage"
mapSaver(
save = {
mapOf(
currentPageKey to it.currentPage,
minPageKey to it.minPage,
maxPageKey to it.maxPage
)
},
restore = {
PagerState(
it[currentPageKey] as Int,
it[minPageKey] as Int,
it[maxPageKey] as Int
)
}
)
}
}
}
@Composable
fun rememberViewPagerState(
@IntRange(from = 0) currentPage: Int = 0,
@IntRange(from = 0) minPage: Int = 0,
@IntRange(from = 0) maxPage: Int = 0
): PagerState = rememberSaveable(saver = PagerState.Saver) {
PagerState(
currentPage = currentPage,
minPage = minPage,
maxPage = maxPage
)
}
@Immutable
private data class PageData(val page: Int) : ParentDataModifier {
override fun Density.modifyParentData(parentData: Any?): Any = this@PageData
}
private val Measurable.page: Int
get() = (parentData as? PageData)?.page ?: error("no PageData for measurable $this")
@Composable
fun ViewPager(
state: PagerState,
modifier: Modifier = Modifier,
offscreenLimit: Int = 2, // the amount of non visible screens to be precomputed to either side of the current page
pageContent: @Composable PagerScope.() -> Unit
) {
var pageSize by rememberSaveable() { mutableStateOf(0) }
val coroutineScope = rememberCoroutineScope()
Layout(
content = {
val minPage = (state.currentPage - offscreenLimit).coerceAtLeast(state.minPage)
val maxPage = (state.currentPage + offscreenLimit).coerceAtMost(state.maxPage)
for (page in minPage..maxPage) {
val pageData = PageData(page)
val scope = PagerScope(page)
key(pageData) {
Box(contentAlignment = Alignment.Center, modifier = pageData) {
scope.pageContent()
}
}
}
},
modifier = modifier.draggable(
orientation = Orientation.Horizontal,
onDragStarted = {
state.selectionState = PagerState.SelectionState.Undecided
},
onDragStopped = { velocity ->
coroutineScope.launch {
// Velocity is in pixels per second, but we deal in percentage offsets, so we
// need to scale the velocity to match
state.fling(velocity / pageSize)
}
},
state = rememberDraggableState { dy ->
coroutineScope.launch {
with(state) {
val pos = pageSize * currentPageOffset
val max = if (currentPage == minPage) 0 else pageSize * offscreenLimit
val min = if (currentPage == maxPage) 0 else -pageSize * offscreenLimit
val newPos = (pos + dy).coerceIn(min.toFloat(), max.toFloat())
snapToOffset(newPos / pageSize)
}
}
},
)
) { measurables, constraints ->
layout(constraints.maxWidth, constraints.maxHeight) {
val currentPage = state.currentPage
val offset = state.currentPageOffset
val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)
measurables
.map {
it.measure(childConstraints) to it.page
}
.forEach { (placeable, page) ->
// TODO: current this centers each page. We should investigate reading
// gravity modifiers on the child, or maybe as a param to Pager.
val yCenterOffset = (constraints.maxHeight - placeable.height) / 2
if (currentPage == page) {
pageSize = placeable.width
}
val xItemOffset = ((page + offset - currentPage) * placeable.width).roundToInt()
placeable.place(
x = xItemOffset,
y = yCenterOffset
)
}
}
}
}
/**
* Scope for [ViewPager] content.
*/
class PagerScope(
val page: Int
)

Просмотреть файл

@ -147,10 +147,10 @@ class DevModeActivity : AppCompatActivity() {
private fun getFinalRadius() =
(max(binding.devRootLayout.width, binding.devRootLayout.height) * RADIUS_MULTIPLIER).toFloat()
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
if (layoutInfoViewModel.isDualMode.value == true) {
menuInflater.inflate(R.menu.dev_mode_menu, menu)
menu?.findItem(R.id.menu_main_user_mode)?.actionView?.setOnClickListener {
menu.findItem(R.id.menu_main_user_mode)?.actionView?.setOnClickListener {
unRevealActivity(it)
}
}

Просмотреть файл

@ -0,0 +1,12 @@
/*
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*
*/
package com.microsoft.device.samples.dualscreenexperience.presentation.util
import android.net.Uri
fun getImageUri(imagePath: String?): Uri? = Uri.parse("file:///android_asset/$imagePath")

Просмотреть файл

@ -9,16 +9,9 @@
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.catalog.CatalogListFragment">
<FrameLayout
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground">
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
android:layout_height="match_parent" />
</layout>

Просмотреть файл

@ -1,161 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item1_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="?android:attr/colorBackground">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_first"
style="@style/Catalog.TableOfContents"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
android:textSize="@dimen/text_size_18"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/text_second"
style="@style/Catalog.TableOfContents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:ellipsize="middle"
android:paddingTop="@dimen/normal_padding"
android:paddingBottom="@dimen/normal_padding"
android:singleLine="true"
android:text="@{catalogItem.secondaryDescription}"
app:clickActionLabel="@{@string/catalog_action_label}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_first"
app:pageContentDescription="@{catalogItem.secondaryDescription}" />
<TextView
android:id="@+id/text_third"
style="@style/Catalog.TableOfContents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:ellipsize="middle"
android:paddingTop="@dimen/normal_padding"
android:paddingBottom="@dimen/normal_padding"
android:singleLine="true"
android:text="@{catalogItem.thirdDescription}"
app:clickActionLabel="@{@string/catalog_action_label}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_second"
app:pageContentDescription="@{catalogItem.thirdDescription}" />
<TextView
android:id="@+id/text_fourth"
style="@style/Catalog.TableOfContents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:ellipsize="middle"
android:paddingTop="@dimen/normal_padding"
android:paddingBottom="@dimen/normal_padding"
android:singleLine="true"
android:text="@{catalogItem.fourthDescription}"
app:clickActionLabel="@{@string/catalog_action_label}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_third"
app:pageContentDescription="@{catalogItem.fourthDescription}" />
<TextView
android:id="@+id/text_fifth"
style="@style/Catalog.TableOfContents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:ellipsize="middle"
android:paddingTop="@dimen/normal_padding"
android:paddingBottom="@dimen/normal_padding"
android:singleLine="true"
android:text="@{catalogItem.fifthDescription}"
app:clickActionLabel="@{@string/catalog_action_label}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_fourth"
app:pageContentDescription="@{catalogItem.fifthDescription}" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,183 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item2_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
android:textSize="@dimen/text_size_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/catalog_flow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/catalog_horizontal_margin"
android:paddingEnd="@dimen/catalog_horizontal_margin"
app:constraint_referenced_ids="image_first, text_second"
app:flow_horizontalGap="@dimen/small_margin"
app:flow_horizontalStyle="packed"
app:flow_verticalGap="@dimen/small_margin"
app:flow_verticalStyle="packed"
app:flow_wrapMode="chain"
app:layout_constraintBottom_toTopOf="@id/text_third"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_first"/>
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
tools:background="@color/catalog_tools_image_background"
tools:ignore="MissingConstraints"
tools:minHeight="@dimen/catalog_max_image_height"/>
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/small_margin"
android:text="@{catalogItem.secondaryDescription}"
tools:ignore="MissingConstraints"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_first, text_second" />
<TextView
android:id="@+id/text_third"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_margin_large"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.thirdDescription}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier1" />
<ImageView
android:id="@+id/image_second"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_margin_small"
android:layout_marginEnd="@dimen/catalog_margin_small"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.secondPictureDescription}"
app:assetImage="@{catalogItem.secondPicture}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/image_third"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_third"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<ImageView
android:id="@+id/image_third"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_margin_small"
android:layout_marginTop="@dimen/catalog_margin_small"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.thirdPictureDescription}"
app:assetImage="@{catalogItem.thirdPicture}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toEndOf="@+id/image_second"
app:layout_constraintTop_toBottomOf="@+id/text_third"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,164 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item3_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintBottom_toTopOf="@id/barrier1"
app:layout_constraintEnd_toStartOf="@id/guideline_vertical"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
app:layout_constraintBottom_toTopOf="@id/barrier1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_first,text_first" />
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_margin_small"
android:text="@{catalogItem.secondaryDescription}"
android:textSize="@dimen/text_size_16"
app:layout_constraintBottom_toTopOf="@id/text_third"
app:layout_constraintEnd_toEndOf="@id/guideline_vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier1" />
<TextView
android:id="@+id/text_third"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_margin_small"
android:text="@{catalogItem.thirdDescription}"
app:layout_constraintEnd_toEndOf="@id/guideline_vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_second" />
<ImageView
android:id="@+id/image_second"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_margin_small"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:contentDescription="@{catalogItem.secondPictureDescription}"
app:assetImage="@{catalogItem.secondPicture}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toTopOf="@id/text_second"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,156 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item4_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/micro_margin"
android:layout_marginBottom="@dimen/small_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintBottom_toTopOf="@id/text_first"
app:layout_constraintEnd_toStartOf="@id/image_second"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_base_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/catalog_tools_image_background" />
<ImageView
android:id="@+id/image_second"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/micro_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.secondPictureDescription}"
app:assetImage="@{catalogItem.secondPicture}"
app:layout_constraintBottom_toBottomOf="@id/image_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_base_min_image_height"
app:layout_constraintStart_toEndOf="@+id/image_first"
app:layout_constraintTop_toTopOf="@id/image_first"
tools:background="@color/catalog_tools_image_background" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_first,image_second" />
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
app:layout_constraintBottom_toTopOf="@id/text_second"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_first" />
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.secondaryDescription}"
android:textSize="@dimen/text_size_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_first" />
<TextView
android:id="@+id/text_third"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.thirdDescription}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_second" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,143 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item5_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
app:layout_constraintBottom_toTopOf="@id/image_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_margin_large"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintBottom_toTopOf="@id/text_second"
app:layout_constraintEnd_toStartOf="@id/image_second"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_first"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<ImageView
android:id="@+id/image_second"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_margin_large"
android:layout_marginTop="@dimen/catalog_margin_large"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:contentDescription="@{catalogItem.secondPictureDescription}"
app:assetImage="@{catalogItem.secondPicture}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_double_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toEndOf="@id/image_first"
app:layout_constraintTop_toBottomOf="@id/text_first"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_double_max_image_height" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_first,image_second" />
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_margin_large"
android:layout_marginEnd="@dimen/catalog_margin_small"
android:text="@{catalogItem.secondaryDescription}"
app:layout_constraintEnd_toStartOf="@id/image_second"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_first" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,121 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item6_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
android:textSize="@dimen/text_size_16"
app:layout_constraintBottom_toTopOf="@id/image_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintBottom_toTopOf="@id/text_second"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_first"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_margin_very_large"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.secondaryDescription}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_first" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~
~ Copyright (c) Microsoft Corporation. All rights reserved.
~ Licensed under the MIT License.
~
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="pageNumber"
type="String" />
<variable
name="catalogItem"
type="com.microsoft.device.samples.dualscreenexperience.domain.catalog.model.CatalogItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/catalog_item_scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:fadeScrollbars="true"
android:scrollbarThumbVertical="@color/primary_gold"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@id/pages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/catalog_item7_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<ImageView
android:id="@+id/image_first"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.firstPictureDescription}"
app:assetImage="@{catalogItem.firstPicture}"
app:layout_constraintBottom_toTopOf="@id/barrier1"
app:layout_constraintEnd_toStartOf="@id/guideline_vertical"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<TextView
android:id="@+id/text_first"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_top_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:text="@{catalogItem.primaryDescription}"
app:layout_constraintBottom_toTopOf="@id/barrier1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="image_first,text_first" />
<TextView
android:id="@+id/text_second"
style="@style/Catalog.Description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginTop="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_margin_normal"
android:text="@{catalogItem.secondaryDescription}"
app:layout_constraintEnd_toEndOf="@id/guideline_vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier1" />
<ImageView
android:id="@+id/image_second"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/catalog_margin_small"
android:layout_marginTop="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:adjustViewBounds="true"
android:contentDescription="@{catalogItem.secondPictureDescription}"
app:assetImage="@{catalogItem.secondPicture}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/catalog_max_image_height"
app:layout_constraintHeight_min="@dimen/catalog_min_image_height"
app:layout_constraintStart_toEndOf="@id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@id/barrier1"
tools:background="@color/catalog_tools_image_background"
tools:minHeight="@dimen/catalog_max_image_height" />
<View
android:id="@+id/horizontal_fold"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/black"
android:visibility="gone"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="@+id/pages"
style="@style/Catalog.Description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/catalog_horizontal_margin"
android:layout_marginEnd="@dimen/catalog_horizontal_margin"
android:layout_marginBottom="@dimen/catalog_margin_small"
android:text="@{pageNumber}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Просмотреть файл

@ -57,6 +57,7 @@
<dimen name="text_size_16">16sp</dimen>
<dimen name="text_size_14">14sp</dimen>
<dimen name="text_size_12">12sp</dimen>
<dimen name="text_size_4">4sp</dimen>
<dimen name="team_link_button_width">84dp</dimen>
<dimen name="team_link_button_height">78dp</dimen>
@ -106,7 +107,15 @@
<dimen name="catalog_top_margin">20dp</dimen>
<dimen name="catalog_double_max_image_height">500dp</dimen>
<dimen name="catalog_max_image_height">250dp</dimen>
<dimen name="catalog_max_image_height_dual_landscape">300dp</dimen>
<dimen name="catalog_page_padding">26dp</dimen>
<dimen name="catalog_min_image_height">150dp</dimen>
<dimen name="catalog_min_image_width">200dp</dimen>
<dimen name="catalog_medium_image_height">200dp</dimen>
<dimen name="catalog_max_image_width">250dp</dimen>
<dimen name="catalog_small_screen_min_image_width">150dp</dimen>
<dimen name="catalog_image_margin_start">52dp</dimen>
<dimen name="catalog_image_margin_end">52dp</dimen>
<dimen name="catalog_base_min_image_height">100dp</dimen>
<dimen name="touch_target_size">48dp</dimen>

Просмотреть файл

@ -6,8 +6,8 @@
*/
ext {
gradlePluginVersion = "7.0.3"
kotlinVersion = '1.6.0'
gradlePluginVersion = '7.2.0'
kotlinVersion = '1.6.10'
compileSdkVersion = 31
buildToolsVersion = '30.0.3'
targetSdkVersion = compileSdkVersion
@ -35,7 +35,7 @@ ext {
]
//AndroidX versions
appCompatVersion = '1.3.1'
appCompatVersion = '1.4.1'
constraintLayoutVersion = '2.1.1'
ktxCoreVersion = '1.6.0'
ktxFragmentVersion = '1.3.6'
@ -44,23 +44,45 @@ ext {
windowManagerVersion = '1.0.0'
lifecycleRuntimeKtxVersion = "2.4.0-rc01"
androidxDependencies = [
appCompat : "androidx.appcompat:appcompat:$appCompatVersion",
constraintLayout : "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion",
ktxCore : "androidx.core:core-ktx:$ktxCoreVersion",
ktxFragment : "androidx.fragment:fragment-ktx:$ktxFragmentVersion",
room : "androidx.room:room-runtime:$roomVersion",
roomCompiler : "androidx.room:room-compiler:$roomVersion",
roomKtx : "androidx.room:room-ktx:$roomVersion",
webkit : "androidx.webkit:webkit:$webkitVersion",
windowManager : "androidx.window:window-java:$windowManagerVersion",
lifecycleRuntimeKtx: "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleRuntimeKtxVersion"
appCompat : "androidx.appcompat:appcompat:$appCompatVersion",
constraintLayout : "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion",
ktxCore : "androidx.core:core-ktx:$ktxCoreVersion",
ktxFragment : "androidx.fragment:fragment-ktx:$ktxFragmentVersion",
room : "androidx.room:room-runtime:$roomVersion",
roomCompiler : "androidx.room:room-compiler:$roomVersion",
roomKtx : "androidx.room:room-ktx:$roomVersion",
webkit : "androidx.webkit:webkit:$webkitVersion",
windowManager : "androidx.window:window-java:$windowManagerVersion",
lifecycleRuntimeKtx : "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleRuntimeKtxVersion",
]
//Jetpack compose dependencies versions
composeUiVersion = "1.1.1"
composeViewModelVersion = '2.4.1'
activityComposeVersion = "1.4.0"
constraintLayoutComposeVersion = "1.0.0"
coilVersion = "2.1.0"
composeDependencies = [
composeUi : "androidx.compose.ui:ui:$composeUiVersion",
composeTooling : "androidx.compose.ui:ui-tooling:$composeUiVersion",
composeFoundation : "androidx.compose.foundation:foundation:$composeUiVersion",
composeMaterial : "androidx.compose.material:material:$composeUiVersion",
composeMaterialIcons : "androidx.compose.material:material-icons-core:$composeUiVersion",
composeMaterialIconsExtended : "androidx.compose.material:material-icons-extended:$composeUiVersion",
composeViewModel : "androidx.lifecycle:lifecycle-viewmodel-compose:$composeViewModelVersion",
composeLiveData : "androidx.compose.runtime:runtime-livedata:$composeUiVersion",
activityCompose : "androidx.activity:activity-compose:$activityComposeVersion",
constraintlayoutCompose : "androidx.constraintlayout:constraintlayout-compose:$constraintLayoutComposeVersion",
coil : "io.coil-kt:coil-compose:$coilVersion"
]
//Google dependencies
materialVersion = '1.4.0'
gsonVersion = '2.9.0'
hiltVersion = '2.39.1'
hiltVersion = '2.42'
mapsVersion = '17.0.1'
mapsKtxVersion = '3.2.0'
ossLicensesVersion = '17.0.0'
@ -85,6 +107,7 @@ ext {
snackbarVersion = "1.0.0-alpha1"
bingMapsVersion = "1.2.0"
windowStateVersion = "1.0.0-alpha04"
microsoftDependencies = [
bingMaps : "com.microsoft.maps:maps-sdk:$bingMapsVersion",
@ -94,6 +117,7 @@ ext {
layouts : "com.microsoft.device.dualscreen:layouts:$layoutsVersion",
wmUtils : "com.microsoft.device.dualscreen:wm-utils:$wmUtilsVersion",
snackbar : "com.microsoft.device.dualscreen:snackbar:$snackbarVersion",
windowState : "com.microsoft.device.dualscreen:windowstate:$windowStateVersion",
]
//UI dependencies
@ -103,7 +127,7 @@ ext {
uiDependencies = [
lottie : "com.airbnb.android:lottie:$lottieVersion",
glide : "com.github.bumptech.glide:glide:$glideVersion",
glideAnnotationProcesor: "com.github.bumptech.glide:compiler:$glideVersion"
glideAnnotationProcesor: "com.github.bumptech.glide:compiler:$glideVersion",
]
//Test dependencies version

6
gradle/wrapper/gradle-wrapper.properties поставляемый
Просмотреть файл

@ -1,6 +1,6 @@
#Thu Jan 28 15:34:03 EET 2021
#Thu May 12 21:29:55 EEST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME