Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d3ba609 authored by George Lin's avatar George Lin
Browse files

[TP] Clock carousel view

The view is to h-scroll the clock previews

Test: Manully scroll the clock previews and make sure the clock is set ClockCarouselViewModelTest
Bug: 262924178
Change-Id: I958641377faa95c0fa21b7b78db9820228cb6103
parent 906d65e2
Loading
Loading
Loading
Loading
+102 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<androidx.constraintlayout.motion.widget.MotionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/motion_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layoutDescription="@xml/carousel_scene">

    <FrameLayout
        android:id="@+id/item_view_0"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginEnd="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/item_view_1"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/item_view_1"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginEnd="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/item_view_2"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/item_view_2"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/item_view_3"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/item_view_2"
        app:layout_constraintTop_toTopOf="parent" />

    <FrameLayout
        android:id="@+id/item_view_4"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginStart="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/item_view_3"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.helper.widget.Carousel
        android:id="@+id/carousel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:carousel_backwardTransition="@+id/backward"
        app:carousel_firstView="@+id/item_view_2"
        app:carousel_forwardTransition="@+id/forward"
        app:carousel_infinite="true"
        app:carousel_nextState="@+id/next"
        app:carousel_previousState="@+id/previous"
        app:constraint_referenced_ids="item_view_0,item_view_1,item_view_2,item_view_3,item_view_4" />

    <!-- The guidelines make sure that only the view in the middle show between the lines  -->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="100dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline_end"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_end="100dp" />
</androidx.constraintlayout.motion.widget.MotionLayout>
 No newline at end of file
+35 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/section_header_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include layout="@layout/section_header" />
    </FrameLayout>

    <com.android.customization.picker.clock.ui.view.ClockCarouselView
        android:id="@+id/image_carousel_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
 No newline at end of file
+133 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2023 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<MotionScene
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:motion="http://schemas.android.com/apk/res-auto">

    <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/next"
        motion:duration="1000"
        android:id="@+id/forward">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:touchAnchorSide="left" />
    </Transition>

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/previous"
        android:id="@+id/backward">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorSide="right" />
    </Transition>

    <ConstraintSet android:id="@+id/previous">
        <Constraint
            android:id="@+id/item_view_0"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintEnd_toStartOf="@id/guideline_start"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            motion:layout_constraintDimensionRatio="1:1"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="@id/guideline_start"
            motion:layout_constraintEnd_toEndOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_2"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintStart_toStartOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/item_view_1"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintEnd_toStartOf="@id/guideline_start"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_2"
            android:layout_width="0dp"
            android:layout_height="0dp"
            motion:layout_constraintDimensionRatio="1:1"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="@id/guideline_start"
            motion:layout_constraintEnd_toEndOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_3"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintStart_toStartOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp" />
    </ConstraintSet>

    <ConstraintSet android:id="@+id/next">
        <Constraint
            android:id="@+id/item_view_2"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintEnd_toStartOf="@id/guideline_start"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_3"
            android:layout_width="0dp"
            android:layout_height="0dp"
            motion:layout_constraintDimensionRatio="1:1"
            motion:layout_constraintHorizontal_bias="0.5"
            motion:layout_constraintStart_toStartOf="@id/guideline_start"
            motion:layout_constraintEnd_toEndOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp"
            android:layout_marginEnd="16dp" />
        <Constraint
            android:id="@+id/item_view_4"
            android:layout_width="100dp"
            android:layout_height="100dp"
            motion:layout_constraintStart_toStartOf="@id/guideline_end"
            motion:layout_constraintTop_toTopOf="parent"
            motion:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginStart="16dp" />
    </ConstraintSet>

</MotionScene>
 No newline at end of file
+5 −1
Original line number Diff line number Diff line
@@ -25,9 +25,13 @@ import kotlinx.coroutines.flow.Flow
 * clocks.
 */
interface ClockPickerRepository {
    val selectedClock: Flow<ClockMetadataModel?>
    val allClocks: Array<ClockMetadataModel>

    val selectedClock: Flow<ClockMetadataModel>

    val selectedClockSize: Flow<ClockSize>

    fun setSelectedClock(clockId: String)

    fun setClockSize(size: ClockSize)
}
+14 −10
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@
 */
package com.android.customization.picker.clock.data.repository

import android.util.Log
import com.android.customization.picker.clock.shared.ClockSize
import com.android.customization.picker.clock.shared.model.ClockMetadataModel
import com.android.systemui.plugins.ClockMetadata
@@ -28,19 +27,24 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.callbackFlow

/** Implementation of [ClockPickerRepository], using [ClockRegistry]. */
class ClockPickerRepositoryImpl(registry: ClockRegistry) : ClockPickerRepository {
class ClockPickerRepositoryImpl(private val registry: ClockRegistry) : ClockPickerRepository {

    override val allClocks: Array<ClockMetadataModel> =
        registry
            .getClocks()
            .filter { "NOT_IN_USE" !in it.clockId }
            .map { it.toModel() }
            .toTypedArray()

    /** The currently-selected clock. */
    override val selectedClock: Flow<ClockMetadataModel?> = callbackFlow {
    override val selectedClock: Flow<ClockMetadataModel> = callbackFlow {
        fun send() {
            val model =
                registry
                    .getClocks()
                    .find { clockMetadata -> clockMetadata.clockId == registry.currentClockId }
                    ?.toModel()
            if (model == null) {
                Log.e(TAG, "Currently selected clock ID is not one of the available clocks.")
            }
            checkNotNull(model)
            trySend(model)
        }

@@ -50,6 +54,10 @@ class ClockPickerRepositoryImpl(registry: ClockRegistry) : ClockPickerRepository
        awaitClose { registry.unregisterClockChangeListener(listener) }
    }

    override fun setSelectedClock(clockId: String) {
        registry.currentClockId = clockId
    }

    // TODO(b/262924055): Use the shared system UI component to query the clock size
    private val _selectedClockSize = MutableStateFlow(ClockSize.DYNAMIC)
    override val selectedClockSize: Flow<ClockSize> = _selectedClockSize.asStateFlow()
@@ -61,8 +69,4 @@ class ClockPickerRepositoryImpl(registry: ClockRegistry) : ClockPickerRepository
    private fun ClockMetadata.toModel(): ClockMetadataModel {
        return ClockMetadataModel(clockId = clockId, name = name)
    }

    companion object {
        private const val TAG = "ClockPickerRepositoryImpl"
    }
}
Loading