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.timer
18 
19 import android.annotation.SuppressLint
20 import android.util.ArrayMap
21 import android.view.View
22 import android.view.ViewGroup
23 import androidx.fragment.app.Fragment
24 import androidx.fragment.app.FragmentManager
25 import androidx.fragment.app.FragmentTransaction
26 import androidx.viewpager.widget.PagerAdapter
27 
28 import com.android.deskclock.data.DataModel
29 import com.android.deskclock.data.Timer
30 import com.android.deskclock.data.TimerListener
31 
32 /**
33  * This adapter produces a [TimerItemFragment] for each timer.
34  */
35 internal class TimerPagerAdapter(
36     private val mFragmentManager: FragmentManager
37 ) : PagerAdapter(), TimerListener {
38 
39     /** Maps each timer id to the corresponding [TimerItemFragment] that draws it.  */
40     private val mFragments: MutableMap<Int, TimerItemFragment?> = ArrayMap()
41 
42     /** The current fragment transaction in play or `null`.  */
43     private var mCurrentTransaction: FragmentTransaction? = null
44 
45     /** The [TimerItemFragment] that is current visible on screen.  */
46     private var mCurrentPrimaryItem: Fragment? = null
47 
getCountnull48     override fun getCount(): Int = timers.size
49 
50     override fun isViewFromObject(view: View, any: Any): Boolean {
51         return (any as Fragment).view === view
52     }
53 
getItemPositionnull54     override fun getItemPosition(any: Any): Int {
55         val fragment = any as TimerItemFragment
56         val timer = fragment.timer
57 
58         val position = timers.indexOf(timer)
59         return if (position == -1) POSITION_NONE else position
60     }
61 
62     @SuppressLint("CommitTransaction")
instantiateItemnull63     override fun instantiateItem(container: ViewGroup, position: Int): Fragment {
64         if (mCurrentTransaction == null) {
65             mCurrentTransaction = mFragmentManager.beginTransaction()
66         }
67 
68         val timer = timers[position]
69 
70         // Search for the existing fragment by tag.
71         val tag = javaClass.simpleName + timer.id
72         var fragment = mFragmentManager.findFragmentByTag(tag) as TimerItemFragment?
73 
74         if (fragment != null) {
75             // Reattach the existing fragment.
76             mCurrentTransaction!!.attach(fragment)
77         } else {
78             // Create and add a new fragment.
79             fragment = TimerItemFragment.newInstance(timer)
80             mCurrentTransaction!!.add(container.id, fragment, tag)
81         }
82 
83         if (fragment !== mCurrentPrimaryItem) {
84             setItemVisible(fragment, false)
85         }
86 
87         mFragments[timer.id] = fragment
88 
89         return fragment
90     }
91 
92     @SuppressLint("CommitTransaction")
destroyItemnull93     override fun destroyItem(container: ViewGroup, position: Int, any: Any) {
94         val fragment = any as TimerItemFragment
95 
96         if (mCurrentTransaction == null) {
97             mCurrentTransaction = mFragmentManager.beginTransaction()
98         }
99 
100         mFragments.remove(fragment.timerId)
101         mCurrentTransaction!!.remove(fragment)
102     }
103 
setPrimaryItemnull104     override fun setPrimaryItem(container: ViewGroup, position: Int, any: Any) {
105         val fragment = any as Fragment
106         if (fragment !== mCurrentPrimaryItem) {
107             mCurrentPrimaryItem?.let {
108                 setItemVisible(it, false)
109             }
110 
111             mCurrentPrimaryItem = fragment
112 
113             mCurrentPrimaryItem?.let {
114                 setItemVisible(it, true)
115             }
116         }
117     }
118 
finishUpdatenull119     override fun finishUpdate(container: ViewGroup) {
120         if (mCurrentTransaction != null) {
121             mCurrentTransaction!!.commitAllowingStateLoss()
122             mCurrentTransaction = null
123 
124             if (!mFragmentManager.isDestroyed) {
125                 mFragmentManager.executePendingTransactions()
126             }
127         }
128     }
129 
timerAddednull130     override fun timerAdded(timer: Timer) {
131         notifyDataSetChanged()
132     }
133 
timerRemovednull134     override fun timerRemoved(timer: Timer) {
135         notifyDataSetChanged()
136     }
137 
timerUpdatednull138     override fun timerUpdated(before: Timer, after: Timer) {
139         val timerItemFragment = mFragments[after.id]
140         timerItemFragment?.updateTime()
141     }
142 
143     /**
144      * @return `true` if at least one timer is in a state requiring continuous updates
145      */
updateTimenull146     fun updateTime(): Boolean {
147         var continuousUpdates = false
148         for (fragment in mFragments.values) {
149             continuousUpdates = continuousUpdates or fragment!!.updateTime()
150         }
151         return continuousUpdates
152     }
153 
getTimernull154     fun getTimer(index: Int): Timer {
155         return timers[index]
156     }
157 
158     private val timers: List<Timer>
159         get() = DataModel.dataModel.timers
160 
161     companion object {
setItemVisiblenull162         private fun setItemVisible(item: Fragment, visible: Boolean) {
163             item.setMenuVisibility(visible)
164             item.setUserVisibleHint(visible)
165         }
166     }
167 }