1 /*
<lambda>null2  * Copyright (C) 2024 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.settings.print
18 
19 import android.app.settings.SettingsEnums
20 import android.content.Context
21 import android.content.Intent
22 import android.net.Uri
23 import android.os.Bundle
24 import android.provider.Settings
25 import androidx.annotation.VisibleForTesting
26 import androidx.compose.foundation.Image
27 import androidx.compose.foundation.layout.Column
28 import androidx.compose.foundation.layout.fillMaxSize
29 import androidx.compose.foundation.layout.padding
30 import androidx.compose.foundation.layout.size
31 import androidx.compose.material.icons.Icons
32 import androidx.compose.material.icons.outlined.Add
33 import androidx.compose.material.icons.outlined.Print
34 import androidx.compose.material3.Icon
35 import androidx.compose.material3.MaterialTheme
36 import androidx.compose.material3.Text
37 import androidx.compose.runtime.Composable
38 import androidx.compose.runtime.getValue
39 import androidx.compose.runtime.remember
40 import androidx.compose.ui.Alignment
41 import androidx.compose.ui.Modifier
42 import androidx.compose.ui.draw.alpha
43 import androidx.compose.ui.platform.LocalContext
44 import androidx.compose.ui.res.stringResource
45 import androidx.compose.ui.unit.dp
46 import androidx.core.os.bundleOf
47 import androidx.lifecycle.compose.collectAsStateWithLifecycle
48 import com.android.settings.R
49 import com.android.settings.core.SubSettingLauncher
50 import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
51 import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
52 import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
53 import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
54 import com.android.settingslib.spa.framework.common.SettingsPageProvider
55 import com.android.settingslib.spa.framework.compose.rememberContext
56 import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
57 import com.android.settingslib.spa.framework.theme.SettingsDimension
58 import com.android.settingslib.spa.framework.theme.SettingsOpacity
59 import com.android.settingslib.spa.widget.preference.Preference
60 import com.android.settingslib.spa.widget.preference.PreferenceModel
61 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
62 import com.android.settingslib.spa.widget.ui.Category
63 import com.android.settingslib.spa.widget.ui.SettingsIcon
64 import com.android.settingslib.spaprivileged.settingsprovider.settingsSecureStringFlow
65 import com.android.settingslib.spaprivileged.template.common.UserProfilePager
66 import kotlinx.coroutines.flow.Flow
67 
68 object PrintSettingsPageProvider : SettingsPageProvider {
69     override val name = "PrintSettings"
70 
71     @Composable
72     override fun Page(arguments: Bundle?) {
73         RegularScaffold(title = stringResource(R.string.print_settings)) {
74             val context = LocalContext.current
75             val printRepository = remember(context) { PrintRepository(context) }
76             UserProfilePager { PrintServices(printRepository) }
77         }
78     }
79 
80     @Composable
81     private fun PrintServices(printRepository: PrintRepository) {
82         val printServiceDisplayInfos by
83             remember { printRepository.printServiceDisplayInfosFlow() }
84                 .collectAsStateWithLifecycle(initialValue = emptyList())
85         if (printServiceDisplayInfos.isEmpty()) {
86             NoServicesInstalled()
87         } else {
88             Category(title = stringResource(R.string.print_settings_title)) {
89                 for (printServiceDisplayInfo in printServiceDisplayInfos) {
90                     PrintService(printServiceDisplayInfo)
91                 }
92             }
93         }
94         AddPrintService()
95     }
96 
97     @Composable
98     private fun NoServicesInstalled() {
99         Column(
100             modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPaddingAround),
101             horizontalAlignment = Alignment.CenterHorizontally,
102         ) {
103             Icon(
104                 imageVector = Icons.Outlined.Print,
105                 contentDescription = null,
106                 modifier =
107                     Modifier.size(110.dp)
108                         .padding(SettingsDimension.itemPaddingAround)
109                         .alpha(SettingsOpacity.SurfaceTone),
110             )
111             Text(
112                 text = stringResource(R.string.print_no_services_installed),
113                 style = MaterialTheme.typography.titleLarge,
114             )
115         }
116     }
117 
118     @VisibleForTesting
119     @Composable
120     fun PrintService(displayInfo: PrintServiceDisplayInfo) {
121         val context = LocalContext.current
122         Preference(
123             object : PreferenceModel {
124                 override val title = displayInfo.title
125                 override val summary = { displayInfo.summary }
126                 override val icon: @Composable () -> Unit = {
127                     Image(
128                         painter = rememberDrawablePainter(displayInfo.icon),
129                         contentDescription = null,
130                         modifier = Modifier.size(SettingsDimension.appIconItemSize),
131                     )
132                 }
133                 override val onClick = { launchPrintServiceSettings(context, displayInfo) }
134             }
135         )
136     }
137 
138     private fun launchPrintServiceSettings(context: Context, displayInfo: PrintServiceDisplayInfo) {
139         SubSettingLauncher(context)
140             .apply {
141                 setDestination(PrintServiceSettingsFragment::class.qualifiedName)
142                 setArguments(
143                     bundleOf(
144                         EXTRA_CHECKED to displayInfo.isEnabled,
145                         EXTRA_TITLE to displayInfo.title,
146                         EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
147                     )
148                 )
149                 setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
150             }
151             .launch()
152     }
153 
154     @Composable
155     fun AddPrintService(
156         searchUriFlow: Flow<String> = rememberContext { context ->
157             context.settingsSecureStringFlow(Settings.Secure.PRINT_SERVICE_SEARCH_URI)
158         },
159     ) {
160         val context = LocalContext.current
161         val searchUri by searchUriFlow.collectAsStateWithLifecycle("")
162         if (searchUri.isEmpty()) return
163         Preference(
164             object : PreferenceModel {
165                 override val title = stringResource(R.string.print_menu_item_add_service)
166                 override val icon = @Composable { SettingsIcon(imageVector = Icons.Outlined.Add) }
167                 override val onClick = {
168                     context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)))
169                 }
170             }
171         )
172     }
173 }
174