1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.deskclock.uidata
18 
19 import android.content.SharedPreferences
20 import android.text.TextUtils
21 import android.view.View
22 
23 import java.util.Locale
24 
25 /**
26  * All tab data is accessed via this model.
27  */
28 internal class TabModel(private val mPrefs: SharedPreferences) {
29 
30     /** The listeners to notify when the selected tab is changed.  */
31     private val mTabListeners: MutableList<TabListener> = ArrayList()
32 
33     /** The listeners to notify when the vertical scroll state of the selected tab is changed.  */
34     private val mTabScrollListeners: MutableList<TabScrollListener> = ArrayList()
35 
36     /** The scrolled-to-top state of each tab.  */
37     private val mTabScrolledToTop = BooleanArray(UiDataModel.Tab.values().size)
38 
39     /** An enumerated value indicating the currently selected tab.  */
40     private var mSelectedTab: UiDataModel.Tab? = null
41 
42     init {
43         mTabScrolledToTop.fill(true)
44     }
45 
46     //
47     // Selected tab
48     //
49 
50     /**
51      * @param tabListener to be notified when the selected tab changes
52      */
addTabListenernull53     fun addTabListener(tabListener: TabListener) {
54         mTabListeners.add(tabListener)
55     }
56 
57     /**
58      * @param tabListener to no longer be notified when the selected tab changes
59      */
removeTabListenernull60     fun removeTabListener(tabListener: TabListener) {
61         mTabListeners.remove(tabListener)
62     }
63 
64     /**
65      * @return the number of tabs
66      */
67     val tabCount: Int
68         get() = UiDataModel.Tab.values().size
69 
70     /**
71      * @param ordinal the ordinal (left-to-right index) of the tab
72      * @return the tab at the given `ordinal`
73      */
getTabnull74     fun getTab(ordinal: Int): UiDataModel.Tab {
75         return UiDataModel.Tab.values()[ordinal]
76     }
77 
78     /**
79      * @param position the position of the tab in the user interface
80      * @return the tab at the given `ordinal`
81      */
getTabAtnull82     fun getTabAt(position: Int): UiDataModel.Tab {
83         val layoutDirection = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
84         val ordinal: Int = if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
85             tabCount - position - 1
86         } else {
87             position
88         }
89         return getTab(ordinal)
90     }
91 
92     /**
93      * @return an enumerated value indicating the currently selected primary tab
94      */
95     val selectedTab: UiDataModel.Tab
96         get() {
97             if (mSelectedTab == null) {
98                 mSelectedTab = TabDAO.getSelectedTab(mPrefs)
99             }
100             return mSelectedTab!!
101         }
102 
103     /**
104      * @param tab an enumerated value indicating the newly selected primary tab
105      */
setSelectedTabnull106     fun setSelectedTab(tab: UiDataModel.Tab) {
107         val oldSelectedTab = selectedTab
108         if (oldSelectedTab != tab) {
109             mSelectedTab = tab
110             TabDAO.setSelectedTab(mPrefs, tab)
111 
112             // Notify of the tab change.
113             for (tl in mTabListeners) {
114                 tl.selectedTabChanged(oldSelectedTab, tab)
115             }
116 
117             // Notify of the vertical scroll position change if there is one.
118             val tabScrolledToTop = isTabScrolledToTop(tab)
119             if (isTabScrolledToTop(oldSelectedTab) != tabScrolledToTop) {
120                 for (tsl in mTabScrollListeners) {
121                     tsl.selectedTabScrollToTopChanged(tab, tabScrolledToTop)
122                 }
123             }
124         }
125     }
126 
127     //
128     // Tab scrolling
129     //
130 
131     /**
132      * @param tabScrollListener to be notified when the scroll position of the selected tab changes
133      */
addTabScrollListenernull134     fun addTabScrollListener(tabScrollListener: TabScrollListener) {
135         mTabScrollListeners.add(tabScrollListener)
136     }
137 
138     /**
139      * @param tabScrollListener to be notified when the scroll position of the selected tab changes
140      */
removeTabScrollListenernull141     fun removeTabScrollListener(tabScrollListener: TabScrollListener) {
142         mTabScrollListeners.remove(tabScrollListener)
143     }
144 
145     /**
146      * Updates the scrolling state in the [UiDataModel] for this tab.
147      *
148      * @param tab an enumerated value indicating the tab reporting its vertical scroll position
149      * @param scrolledToTop `true` iff the vertical scroll position of this tab is at the top
150      */
setTabScrolledToTopnull151     fun setTabScrolledToTop(tab: UiDataModel.Tab, scrolledToTop: Boolean) {
152         if (isTabScrolledToTop(tab) != scrolledToTop) {
153             mTabScrolledToTop[tab.ordinal] = scrolledToTop
154             if (tab == selectedTab) {
155                 for (tsl in mTabScrollListeners) {
156                     tsl.selectedTabScrollToTopChanged(tab, scrolledToTop)
157                 }
158             }
159         }
160     }
161 
162     /**
163      * @param tab identifies the tab
164      * @return `true` iff the content in the given `tab` is currently scrolled to top
165      */
isTabScrolledToTopnull166     fun isTabScrolledToTop(tab: UiDataModel.Tab): Boolean {
167         return mTabScrolledToTop[tab.ordinal]
168     }
169 }