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