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.healthconnect.controller.exportimport
18 
19 import android.app.Activity
20 import android.content.Intent
21 import android.os.Bundle
22 import android.provider.DocumentsContract
23 import android.view.LayoutInflater
24 import android.view.View
25 import android.view.View.GONE
26 import android.view.View.VISIBLE
27 import android.view.ViewGroup
28 import android.widget.Button
29 import android.widget.ImageView
30 import android.widget.TextView
31 import android.widget.Toast
32 import androidx.activity.result.ActivityResult
33 import androidx.activity.result.ActivityResultLauncher
34 import androidx.activity.result.contract.ActivityResultContracts
35 import androidx.fragment.app.Fragment
36 import androidx.fragment.app.viewModels
37 import androidx.navigation.fragment.findNavController
38 import com.android.healthconnect.controller.R
39 import com.android.healthconnect.controller.exportimport.api.DocumentProviders
40 import com.android.healthconnect.controller.exportimport.api.ExportSettingsViewModel
41 import com.android.healthconnect.controller.exportimport.api.isLocalFile
42 import com.android.healthconnect.controller.utils.DeviceInfoUtils
43 import com.android.settingslib.widget.LinkTextView
44 import dagger.hilt.android.AndroidEntryPoint
45 import javax.inject.Inject
46 
47 /** Fragment to allow the user to find and select the backup file to import and restore. */
48 @AndroidEntryPoint(Fragment::class)
49 class ImportSourceLocationFragment : Hilt_ImportSourceLocationFragment() {
50     private val contract = ActivityResultContracts.StartActivityForResult()
51     private val saveResultLauncher: ActivityResultLauncher<Intent> =
52         registerForActivityResult(contract, ::onSave)
53 
54     private val viewModel: ExportSettingsViewModel by viewModels()
55 
56     @Inject lateinit var deviceInfoUtils: DeviceInfoUtils
57 
58     override fun onCreateView(
59         inflater: LayoutInflater,
60         container: ViewGroup?,
61         savedInstanceState: Bundle?
62     ): View? {
63         val view = inflater.inflate(R.layout.import_source_location_screen, container, false)
64         val pageHeaderView = view.findViewById<TextView>(R.id.page_header_text)
65         val pageHeaderIconView = view.findViewById<ImageView>(R.id.page_header_icon)
66         val footerView = view.findViewById<View>(R.id.export_import_footer)
67         val playStoreView = view.findViewById<LinkTextView>(R.id.export_import_go_to_play_store)
68         val cancelButton = view.findViewById<Button>(R.id.export_import_cancel_button)
69         val nextButton = view.findViewById<Button>(R.id.export_import_next_button)
70 
71         pageHeaderView.text = getString(R.string.import_source_location_title)
72         pageHeaderIconView.setImageResource(R.drawable.ic_import_data)
73         nextButton.text = getString(R.string.import_next_button)
74         cancelButton.text = getString(R.string.import_cancel_button)
75 
76         cancelButton.setOnClickListener { requireActivity().finish() }
77 
78         if (deviceInfoUtils.isPlayStoreAvailable(requireContext())) {
79             playStoreView?.setVisibility(VISIBLE)
80             playStoreView?.setOnClickListener {
81                 findNavController().navigate(R.id.action_importSourceLocationFragment_to_playStore)
82             }
83         }
84 
85         val documentProvidersViewBinder = DocumentProvidersViewBinder()
86         val documentProvidersList = view.findViewById<ViewGroup>(R.id.import_document_providers)
87         viewModel.documentProviders.observe(viewLifecycleOwner) { providers ->
88             documentProvidersList.removeAllViews()
89             nextButton.setOnClickListener {}
90             nextButton.setEnabled(false)
91 
92             when (providers) {
93                 is DocumentProviders.Loading -> {
94                     // Do nothing
95                 }
96                 is DocumentProviders.LoadingFailed -> {
97                     Toast.makeText(activity, R.string.default_error, Toast.LENGTH_LONG).show()
98                 }
99                 is DocumentProviders.WithData -> {
100                     documentProvidersViewBinder.bindDocumentProvidersView(
101                         providers.providers, documentProvidersList, inflater) { root ->
102                             nextButton.setOnClickListener {
103                                 saveResultLauncher.launch(
104                                     Intent(Intent.ACTION_OPEN_DOCUMENT)
105                                         .addFlags(
106                                             Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
107                                                 Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
108                                         .setType("application/zip")
109                                         .addCategory(Intent.CATEGORY_OPENABLE)
110                                         .putExtra(DocumentsContract.EXTRA_INITIAL_URI, root.uri))
111                             }
112                             nextButton.setEnabled(true)
113                         }
114 
115                     if (providers.providers.size > 1) {
116                         footerView.setVisibility(GONE)
117                     } else {
118                         footerView.setVisibility(VISIBLE)
119                     }
120                 }
121             }
122         }
123 
124         return view
125     }
126 
127     private fun onSave(result: ActivityResult) {
128         if (result.resultCode == Activity.RESULT_OK) {
129             val fileUri = result.data?.data ?: return
130             if (isLocalFile(fileUri)) {
131                 Toast.makeText(activity, R.string.import_invalid_storage, Toast.LENGTH_LONG).show()
132             } else {
133                 // TODO: b/339189778 - Add test when import API is done.
134                 findNavController()
135                     .navigate(R.id.action_importSourceLocationFragment_to_importDecryptionFragment)
136             }
137         }
138     }
139 }
140