• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ACTION_START_DEVICE_FINANCING_DEFERRED_PROVISIONING;
20 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_FINANCING_PROVISIONING;
21 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_FINANCING_SECONDARY_USER_PROVISIONING;
22 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_SUBSIDY_DEFERRED_PROVISIONING;
23 import static com.android.devicelockcontroller.common.DeviceLockConstants.ACTION_START_DEVICE_SUBSIDY_PROVISIONING;
24 
25 import static com.google.common.base.Preconditions.checkNotNull;
26 
27 import android.Manifest;
28 import android.app.PendingIntent;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.os.Bundle;
32 import android.text.TextUtils;
33 import android.view.LayoutInflater;
34 import android.view.View;
35 import android.view.ViewGroup;
36 import android.widget.Button;
37 import android.widget.ImageView;
38 import android.widget.TextView;
39 import android.widget.Toast;
40 
41 import androidx.activity.result.ActivityResultLauncher;
42 import androidx.activity.result.contract.ActivityResultContracts;
43 import androidx.annotation.NonNull;
44 import androidx.annotation.Nullable;
45 import androidx.core.content.ContextCompat;
46 import androidx.fragment.app.Fragment;
47 import androidx.lifecycle.ViewModelProvider;
48 import androidx.recyclerview.widget.RecyclerView;
49 import androidx.work.WorkManager;
50 
51 import com.android.devicelockcontroller.R;
52 import com.android.devicelockcontroller.provision.worker.PauseProvisioningWorker;
53 import com.android.devicelockcontroller.util.LogUtil;
54 
55 import java.time.Duration;
56 import java.time.Instant;
57 import java.util.Objects;
58 
59 /**
60  * The screen that provides information about the provision.
61  */
62 public final class ProvisionInfoFragment extends Fragment {
63 
64     private static final String TAG = "ProvisionInfoFragment";
65 
66     private ActivityResultLauncher<String> requestPermissionLauncher =
67             registerForActivityResult(new ActivityResultContracts.RequestPermission(),
68                     isGranted -> {
69                             if (isGranted) {
70                                 createNotificationAndCloseActivity();
71                             } else {
72                                 Toast.makeText(getActivity(),
73                                         R.string.toast_message_grant_notification_permission,
74                                         Toast.LENGTH_LONG).show();
75                             }
76                     }
77             );
78 
79     @Nullable
80     @Override
onCreateView( @onNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)81     public View onCreateView(
82             @NonNull LayoutInflater inflater,
83             @Nullable ViewGroup container,
84             @Nullable Bundle savedInstanceState) {
85         return inflater.inflate(R.layout.fragment_provision_info, container, false);
86     }
87 
88     @Override
onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)89     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
90         super.onViewCreated(view, savedInstanceState);
91         ProvisionInfoViewModel viewModel;
92         boolean isDeferredProvisioning = false;
93         switch (Objects.requireNonNull(getActivity()).getIntent().getAction()) {
94             case ACTION_START_DEVICE_FINANCING_PROVISIONING:
95                 viewModel = new ViewModelProvider(this).get(
96                         DeviceFinancingProvisionInfoViewModel.class);
97                 break;
98             case ACTION_START_DEVICE_FINANCING_DEFERRED_PROVISIONING:
99                 viewModel = new ViewModelProvider(this).get(
100                         DeviceFinancingDeferredProvisionInfoViewModel.class);
101                 isDeferredProvisioning = true;
102                 break;
103             case ACTION_START_DEVICE_FINANCING_SECONDARY_USER_PROVISIONING:
104                 viewModel = new ViewModelProvider(this).get(
105                         DeviceFinancingSecondaryUserProvisionInfoViewModel.class);
106                 break;
107             case ACTION_START_DEVICE_SUBSIDY_PROVISIONING:
108                 viewModel = new ViewModelProvider(this).get(
109                         DeviceSubsidyProvisionInfoViewModel.class);
110                 break;
111             case ACTION_START_DEVICE_SUBSIDY_DEFERRED_PROVISIONING:
112                 viewModel = new ViewModelProvider(this).get(
113                         DeviceSubsidyDeferredProvisionInfoViewModel.class);
114                 isDeferredProvisioning = true;
115                 break;
116             default:
117                 LogUtil.e(TAG, "Unknown action is received, exiting");
118                 return;
119         }
120 
121         RecyclerView recyclerView = view.findViewById(R.id.recyclerview_provision_info);
122         if (recyclerView == null) {
123             LogUtil.e(TAG, "Could not find provision info RecyclerView, should not reach here.");
124             return;
125         }
126         ProvisionInfoListAdapter adapter = new ProvisionInfoListAdapter(viewModel,
127                 getViewLifecycleOwner());
128         viewModel.mProvisionInfoListLiveData.observe(getViewLifecycleOwner(),
129                 adapter::submitList);
130         recyclerView.setAdapter(adapter);
131         ImageView imageView = view.findViewById(R.id.header_icon);
132         if (imageView == null) {
133             LogUtil.e(TAG, "Could not find header ImageView, should not reach here.");
134             return;
135         }
136         viewModel.mHeaderDrawableIdLiveData.observe(getViewLifecycleOwner(),
137                 imageView::setImageResource);
138 
139         TextView headerTextView = view.findViewById(R.id.header_text);
140         if (headerTextView == null) {
141             LogUtil.e(TAG, "Could not find header TextView, should not reach here.");
142             return;
143         }
144         viewModel.mHeaderTextLiveData.observe(getViewLifecycleOwner(),
145                 pair -> {
146                     if (pair.first > 0 && !TextUtils.isEmpty(pair.second)) {
147                         headerTextView.setText(getString(pair.first, pair.second));
148                     }
149                 });
150 
151         TextView subheaderTextView = view.findViewById(R.id.subheader_text);
152         if (subheaderTextView == null) {
153             LogUtil.e(TAG, "Could not find subheader TextView, should not reach here.");
154             return;
155         }
156         viewModel.mSubHeaderTextLiveData.observe(getViewLifecycleOwner(),
157                 pair -> {
158                     if (pair.first > 0 && !TextUtils.isEmpty(pair.second)) {
159                         headerTextView.setText(getString(pair.first, pair.second));
160                     }
161                 });
162         Button next = view.findViewById(R.id.button_next);
163         checkNotNull(next);
164         if (isDeferredProvisioning) {
165             next.setText(R.string.start);
166         }
167         next.setOnClickListener(
168                 v -> startActivity(new Intent(getContext(), ProvisioningActivity.class)));
169         updatePreviousButton(checkNotNull(view.findViewById(R.id.button_previous)), viewModel,
170                 isDeferredProvisioning);
171     }
172 
updatePreviousButton(Button previous, ProvisionInfoViewModel viewModel, boolean isDeferredProvisioning)173     private void updatePreviousButton(Button previous, ProvisionInfoViewModel viewModel,
174             boolean isDeferredProvisioning) {
175         if (!isDeferredProvisioning) {
176             previous.setVisibility(View.GONE);
177             return;
178         }
179         previous.setText(R.string.do_it_in_one_hour);
180         previous.setVisibility(View.VISIBLE);
181 
182         viewModel.mIsProvisionForcedLiveData.observe(getViewLifecycleOwner(),
183                 isProvisionForced -> {
184                     previous.setEnabled(!isProvisionForced);
185                     // Allow the user to defer provisioning only when provisioning is not forced.
186                     if (!isProvisionForced) {
187                         previous.setOnClickListener(
188                                 v -> {
189                                     WorkManager workManager =
190                                             WorkManager.getInstance(requireContext());
191                                     PauseProvisioningWorker
192                                             .reportProvisionPausedByUser(workManager);
193                                     int notificationPermission = ContextCompat.checkSelfPermission(
194                                             requireContext(),
195                                             Manifest.permission.POST_NOTIFICATIONS);
196                                     if (PackageManager.PERMISSION_GRANTED
197                                             == notificationPermission) {
198                                         createNotificationAndCloseActivity();
199                                     } else {
200                                         requestPermissionLauncher.launch(
201                                                 Manifest.permission.POST_NOTIFICATIONS);
202                                     }
203                                 });
204                     }
205                 });
206     }
207 
createNotificationAndCloseActivity()208     private void createNotificationAndCloseActivity() {
209         PendingIntent intent = PendingIntent.getActivity(
210                 requireContext(),
211                 /* requestCode= */ 0,
212                 getActivity().getIntent(),
213                 PendingIntent.FLAG_IMMUTABLE);
214         Instant resumeTime = Instant.now().plus(Duration.ofHours(1));
215         DeviceLockNotificationManager.sendDeferredEnrollmentNotification(requireContext(),
216                 resumeTime, intent);
217         getActivity().finish();
218     }
219 }
220