1 /*
2  * Copyright (C) 2023 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.devicelockcontroller.activities;
18 
19 import static com.android.devicelockcontroller.common.DeviceLockConstants.MANDATORY_PROVISION_DEVICE_RESET_COUNTDOWN_MINUTE;
20 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionEvent.PROVISION_FAILURE;
21 import static com.android.devicelockcontroller.policy.ProvisionStateController.ProvisionState.PROVISION_FAILED;
22 
23 import static com.google.common.base.Preconditions.checkNotNull;
24 
25 import android.content.Context;
26 import android.os.Bundle;
27 import android.os.SystemClock;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.widget.Button;
32 import android.widget.Chronometer;
33 import android.widget.ImageView;
34 import android.widget.ProgressBar;
35 import android.widget.TextView;
36 
37 import androidx.annotation.Nullable;
38 import androidx.annotation.VisibleForTesting;
39 import androidx.fragment.app.Fragment;
40 import androidx.lifecycle.ViewModelProvider;
41 import androidx.work.WorkManager;
42 
43 import com.android.devicelockcontroller.R;
44 import com.android.devicelockcontroller.activities.util.UrlUtils;
45 import com.android.devicelockcontroller.policy.PolicyObjectsProvider;
46 import com.android.devicelockcontroller.policy.ProvisionHelper;
47 import com.android.devicelockcontroller.policy.ProvisionHelperImpl;
48 import com.android.devicelockcontroller.policy.ProvisionStateController;
49 import com.android.devicelockcontroller.provision.worker.ReportDeviceProvisionStateWorker;
50 import com.android.devicelockcontroller.util.LogUtil;
51 
52 import com.google.common.util.concurrent.FutureCallback;
53 import com.google.common.util.concurrent.Futures;
54 
55 import java.util.concurrent.TimeUnit;
56 
57 /**
58  * A screen which always displays a progress bar.
59  */
60 public final class ProgressFragment extends Fragment {
61 
62     private static final String TAG = ProgressFragment.class.getSimpleName();
63 
64     private ProvisionHelper mProvisionHelper;
65 
ProgressFragment()66     public ProgressFragment() {
67         super();
68     }
69 
70     @VisibleForTesting
ProgressFragment(ProvisionHelper provisionHelper)71     ProgressFragment(ProvisionHelper provisionHelper) {
72         super();
73         mProvisionHelper = provisionHelper;
74     }
75 
76     @Nullable
77     @Override
onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)78     public View onCreateView(
79             LayoutInflater inflater,
80             @Nullable ViewGroup container,
81             @Nullable Bundle savedInstanceState) {
82         View v = inflater.inflate(R.layout.fragment_progress, container, false);
83 
84         ImageView headerIconImageView = v.findViewById(R.id.header_icon);
85         checkNotNull(headerIconImageView);
86 
87         TextView headerTextView = v.findViewById(R.id.header_text);
88         checkNotNull(headerTextView);
89 
90         TextView subheaderTextView = v.findViewById(R.id.subheader_text);
91         checkNotNull(subheaderTextView);
92 
93         ProgressBar progressBar = v.findViewById(R.id.progress_bar);
94         checkNotNull(progressBar);
95 
96         View bottomView = v.findViewById(R.id.bottom);
97         checkNotNull(bottomView);
98 
99         Chronometer countDownTimeView = v.findViewById(R.id.countdown_text);
100         checkNotNull(countDownTimeView);
101 
102         ProvisioningProgressViewModel provisioningProgressViewModel =
103                 new ViewModelProvider(requireActivity()).get(ProvisioningProgressViewModel.class);
104         provisioningProgressViewModel.getProvisioningProgressLiveData().observe(
105                 getViewLifecycleOwner(), provisioningProgress -> {
106                     if (provisioningProgress.mIconId != 0) {
107                         headerIconImageView.setImageResource(provisioningProgress.mIconId);
108                     }
109                     Context context = requireContext();
110                     if (provisioningProgress.mHeaderId != 0) {
111                         headerTextView.setText(
112                                 context.getString(provisioningProgress.mHeaderId,
113                                         provisioningProgressViewModel
114                                                 .mProviderNameLiveData.getValue()));
115                     }
116                     if (provisioningProgress.mSubheaderId != 0) {
117                         UrlUtils.setUrlText(subheaderTextView,
118                                 context.getString(provisioningProgress.mSubheaderId,
119                                         provisioningProgressViewModel
120                                                 .mProviderNameLiveData
121                                                 .getValue(),
122                                         provisioningProgressViewModel
123                                                 .mSupportUrlLiveData
124                                                 .getValue()));
125                     }
126                     if (provisioningProgress.mProgressBarVisible) {
127                         progressBar.setVisibility(View.VISIBLE);
128                     } else {
129                         progressBar.setVisibility(View.GONE);
130                     }
131                     if (provisioningProgress.mBottomViewVisible) {
132                         bottomView.setVisibility(View.VISIBLE);
133                         Button retryButton = bottomView.findViewById(R.id.button_retry);
134                         checkNotNull(retryButton);
135                         PolicyObjectsProvider policyObjects =
136                                 (PolicyObjectsProvider) context.getApplicationContext();
137                         ProvisionStateController provisionStateController =
138                                 policyObjects.getProvisionStateController();
139                         if (mProvisionHelper == null) {
140                             mProvisionHelper = new ProvisionHelperImpl(
141                                     context,
142                                     provisionStateController);
143                         }
144                         retryButton.setOnClickListener(
145                                 view -> mProvisionHelper.scheduleKioskAppInstallation(
146                                         requireActivity(),
147                                         provisioningProgressViewModel,
148                                         /* isProvisionMandatory= */ false));
149 
150                         Button exitButton = bottomView.findViewById(R.id.button_exit);
151                         checkNotNull(exitButton);
152                         FutureCallback<Integer> getProvisionStateCallback =
153                                 new FutureCallback<>() {
154                                     @Override
155                                     public void onSuccess(Integer result) {
156                                         if (result == PROVISION_FAILED) {
157                                             // Already reported set up failure. Finish normally
158                                             getActivity().finish();
159                                             return;
160                                         }
161                                         ReportDeviceProvisionStateWorker.reportSetupFailed(
162                                                 WorkManager.getInstance(requireContext()),
163                                                 provisioningProgress.mFailureReason);
164                                         provisionStateController.postSetNextStateForEventRequest(
165                                                 PROVISION_FAILURE);
166                                     }
167 
168                                     @Override
169                                     public void onFailure(Throwable t) {
170                                         LogUtil.e(TAG, "Failed to get provision state", t);
171                                     }
172                                 };
173                         exitButton.setOnClickListener(
174                                 view -> Futures.addCallback(provisionStateController.getState(),
175                                         getProvisionStateCallback,
176                                         context.getMainExecutor()));
177                     } else {
178                         bottomView.setVisibility(View.GONE);
179                     }
180                     if (provisioningProgress.mCountDownTimerVisible) {
181                         countDownTimeView.setBase(
182                                 SystemClock.elapsedRealtime() + TimeUnit.MINUTES.toMillis(
183                                         MANDATORY_PROVISION_DEVICE_RESET_COUNTDOWN_MINUTE));
184                         countDownTimeView.start();
185                         countDownTimeView.setVisibility(View.VISIBLE);
186                     } else {
187                         countDownTimeView.setVisibility(View.GONE);
188                     }
189                 });
190         return v;
191     }
192 }
193