• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package com.android.healthconnect.controller.shared.preference
15 
16 import android.content.Context
17 import android.icu.text.MessageFormat
18 import android.view.View
19 import android.widget.FrameLayout
20 import android.widget.LinearLayout
21 import android.widget.TextView
22 import androidx.core.view.isVisible
23 import androidx.preference.PreferenceViewHolder
24 import com.android.healthconnect.controller.R
25 import com.android.healthconnect.controller.shared.app.AppMetadata
26 import com.android.healthconnect.controller.shared.app.AppUtils
27 import com.android.settingslib.widget.SettingsThemeHelper
28 import java.text.NumberFormat
29 import java.util.Locale
30 
31 /**
32  * A custom `Preference` that represents a ranked item with an action icon.
33  *
34  * This preference is designed for use in lists where items have a specific order (ranking) and
35  * require an action menu for reordering or other operations. It displays:
36  * - A position number indicating its rank, 1-based indexing.
37  * - A label (e.g., app name).
38  * - A clickable action icon for additional options (optional).
39  *
40  * @param context The context in which this preference is used.
41  * @param appMetadata Metadata about the item (e.g., app details).
42  * @param appUtils To determine whether a package name is the default app.
43  * @param position The current position of the item in the ranked list, 0-based indexing.
44  * @param onActionClick A lambda that handles action clicks (e.g., reordering, removing items).
45  * @param shouldShowActionButton Whether the action button should be shown, by default true.
46  */
47 class RankedActionPreference(
48     context: Context,
49     private val appMetadata: AppMetadata,
50     private val appUtils: AppUtils,
51     private var position: Int,
52     private val onActionClick: (View, Int) -> Unit,
53     private var shouldShowActionButton: Boolean = true,
54 ) : HealthPreference(context, null) {
55 
56     private lateinit var rootView: LinearLayout
57     private lateinit var appPositionView: TextView
58     private lateinit var actionView: FrameLayout
59 
60     init {
61         layoutResource =
62             if (SettingsThemeHelper.isExpressiveTheme(context)) {
63                 R.layout.widget_app_source_layout_expressive
64             } else {
65                 R.layout.widget_app_source_layout_legacy
66             }
67         order = position
68         isSelectable = false
69     }
70 
71     override fun onBindViewHolder(holder: PreferenceViewHolder) {
72         super.onBindViewHolder(holder)
73 
74         rootView = holder.itemView as LinearLayout
75         appPositionView = holder.findViewById(R.id.app_position) as TextView
76         val appNameView = holder.findViewById(R.id.app_name) as TextView
77         val appSummary = holder.findViewById(R.id.app_source_summary) as TextView
78         actionView = holder.findViewById(R.id.action_icon) as FrameLayout
79 
80         setPositionString()
81         appNameView.text = appMetadata.appName
82         setAppSummary(appSummary)
83         setActionButton()
84         setRootContentDescription()
85     }
86 
87     fun reduceIndex() {
88         position--
89         setPositionString()
90     }
91 
92     fun hideActionButton() {
93         shouldShowActionButton = false
94         actionView.isVisible = false
95     }
96 
97     private fun setPositionString() {
98         val positionString: String = NumberFormat.getIntegerInstance().format(position + 1)
99         appPositionView.text = positionString
100     }
101 
102     private fun setAppSummary(appSummary: TextView) {
103         if (isDefaultApp()) {
104             appSummary.text = context.getString(R.string.default_app_summary)
105             appSummary.visibility = View.VISIBLE
106         } else {
107             appSummary.visibility = View.GONE
108         }
109     }
110 
111     private fun setActionButton() {
112         if (shouldShowActionButton) {
113             actionView.isVisible = true
114             actionView.setOnClickListener { view -> onActionClick(view, position) }
115             actionView.contentDescription =
116                 context.getString(
117                     R.string.see_actions_button_content_description,
118                     appMetadata.appName,
119                 )
120         } else {
121             actionView.isVisible = false
122         }
123     }
124 
125     private fun setRootContentDescription() {
126         rootView.let {
127             it.isClickable = false
128             it.isLongClickable = false
129             it.isFocusable = false
130             it.contentDescription = getContentDescription()
131         }
132     }
133 
134     private fun getContentDescription(): String {
135         val formatter = MessageFormat("{0,ordinal}", Locale.getDefault())
136         val ordinal = formatter.format(arrayOf(position + 1))
137         val description =
138             context.getString(
139                 R.string.priority_list_item_content_description,
140                 ordinal,
141                 appMetadata.appName,
142             )
143         return if (isDefaultApp()) {
144             description + "\n" + context.getString(R.string.default_app_summary)
145         } else {
146             description
147         }
148     }
149 
150     private fun isDefaultApp(): Boolean = appUtils.isDefaultApp(context, appMetadata.packageName)
151 }
152