1 /*
2  * Copyright 2019 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 androidx.navigation.dynamicfeatures.fragment.ui
18 
19 import android.content.ComponentName
20 import android.content.pm.PackageManager
21 import android.os.Bundle
22 import android.util.Log
23 import android.view.View
24 import android.widget.Button
25 import android.widget.ImageView
26 import android.widget.ProgressBar
27 import android.widget.TextView
28 import androidx.annotation.StringRes
29 import androidx.navigation.dynamicfeatures.fragment.R
30 import androidx.navigation.fragment.findNavController
31 import com.google.android.play.core.splitinstall.model.SplitInstallErrorCode
32 
33 /**
34  * The default [androidx.fragment.app.Fragment] to display during installation progress.
35  *
36  * This `Fragment` provides a default UI and handles split install state changes so you don't have
37  * to deal with this.
38  *
39  * To create a custom progress fragment, extend [AbstractProgressFragment].
40  */
41 public class DefaultProgressFragment :
42     AbstractProgressFragment(R.layout.dynamic_feature_install_fragment) {
43 
44     internal companion object {
45         private const val PROGRESS_MAX = 100
46         private const val TAG = "DefaultProgressFragment"
47     }
48 
49     private var title: TextView? = null
50     private var progressBar: ProgressBar? = null
51     private var action: Button? = null
52 
onViewCreatednull53     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
54         super.onViewCreated(view, savedInstanceState)
55         with(view) {
56             title = findViewById(R.id.progress_title)
57             progressBar = findViewById(R.id.installation_progress)
58             setActivityIcon(findViewById(R.id.progress_icon))
59             action = findViewById(R.id.progress_action)
60         }
61     }
62 
onDestroyViewnull63     override fun onDestroyView() {
64         super.onDestroyView()
65         title = null
66         progressBar = null
67         action = null
68     }
69 
setActivityIconnull70     private fun setActivityIcon(activityIcon: ImageView) {
71         with(requireContext().packageManager) {
72             val icon =
73                 try {
74                     getActivityIcon(ComponentName(requireContext(), requireActivity().javaClass))
75                 } catch (e: PackageManager.NameNotFoundException) {
76                     defaultActivityIcon
77                 }
78             activityIcon.setImageDrawable(icon)
79         }
80     }
81 
onProgressnull82     override fun onProgress(status: Int, bytesDownloaded: Long, bytesTotal: Long) {
83         progressBar?.run {
84             visibility = View.VISIBLE
85             if (bytesTotal == 0L) {
86                 isIndeterminate = true
87             } else {
88                 progress = (PROGRESS_MAX * bytesDownloaded / bytesTotal).toInt()
89                 isIndeterminate = false
90             }
91         }
92     }
93 
onCancellednull94     override fun onCancelled() {
95         displayErrorState(R.string.installation_cancelled)
96         displayAction(R.string.retry) { navigate() }
97     }
98 
onFailednull99     override fun onFailed(@SplitInstallErrorCode errorCode: Int) {
100         Log.w(TAG, "Installation failed with error $errorCode")
101         displayErrorState(R.string.installation_failed)
102         displayAction(R.string.ok) { findNavController().popBackStack() }
103     }
104 
105     /** Display an error state message. */
displayErrorStatenull106     private fun displayErrorState(@StringRes text: Int) {
107         title?.setText(text)
108         progressBar?.visibility = View.INVISIBLE
109     }
110 
111     /** Display the action button and assign `onClick` behavior. */
displayActionnull112     private fun displayAction(@StringRes text: Int, onClick: () -> Unit) {
113         action?.run {
114             setText(text)
115             setOnClickListener { onClick() }
116             visibility = View.VISIBLE
117         }
118     }
119 }
120