1 /* 2 * Copyright (C) 2021 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.settings.applications.intentpicker; 18 19 import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE; 20 21 import android.app.Dialog; 22 import android.app.settings.SettingsEnums; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.pm.verify.domain.DomainOwner; 26 import android.content.pm.verify.domain.DomainVerificationManager; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.SystemClock; 31 import android.util.Log; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.widget.ProgressBar; 35 36 import androidx.annotation.NonNull; 37 import androidx.appcompat.app.AlertDialog; 38 import androidx.fragment.app.FragmentManager; 39 import androidx.lifecycle.ViewModelProvider; 40 41 import com.android.settings.R; 42 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 43 import com.android.settingslib.utils.ThreadUtils; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 import java.util.SortedSet; 49 50 /** A customized {@link InstrumentedDialogFragment} with a progress bar. */ 51 public class ProgressDialogFragment extends InstrumentedDialogFragment { 52 private static final String TAG = "ProgressDialogFragment"; 53 private static final String DLG_ID = "ProgressDialog"; 54 private static final int PROGRESS_BAR_STEPPING_TIME = 20; 55 56 private ProgressAlertDialog mProgressAlertDialog; 57 private DomainVerificationManager mDomainVerificationManager; 58 private List<SupportedLinkWrapper> mSupportedLinkWrapperList; 59 private SupportedLinkViewModel mViewModel; 60 private Handler mHandle; 61 private String mPackage; 62 63 @Override onCreate(Bundle savedInstanceState)64 public void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 mViewModel = new ViewModelProvider(this.getActivity()).get(SupportedLinkViewModel.class); 67 } 68 69 @Override onCreateDialog(Bundle savedInstanceState)70 public Dialog onCreateDialog(Bundle savedInstanceState) { 71 mPackage = getArguments().getString(AppLaunchSettings.APP_PACKAGE_KEY); 72 mDomainVerificationManager = getActivity().getSystemService( 73 DomainVerificationManager.class); 74 mHandle = new Handler(Looper.getMainLooper()); 75 mProgressAlertDialog = createProgressAlertDialog(); 76 return mProgressAlertDialog; 77 } 78 createProgressAlertDialog()79 private ProgressAlertDialog createProgressAlertDialog() { 80 final Context context = getActivity(); 81 final ProgressAlertDialog progressDialog = new ProgressAlertDialog(context); 82 final String title = context.getResources().getString( 83 R.string.app_launch_checking_links_title); 84 progressDialog.setTitle(IntentPickerUtils.getCentralizedDialogTitle(title)); 85 progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, 86 context.getText(R.string.app_launch_dialog_cancel), 87 (dialog, which) -> { 88 if (which == DialogInterface.BUTTON_NEGATIVE) { 89 dialog.cancel(); 90 } 91 }); 92 progressDialog.setCanceledOnTouchOutside(true); 93 return progressDialog; 94 } 95 96 /** Display the {@link ProgressAlertDialog}. */ showDialog(FragmentManager manager)97 public void showDialog(FragmentManager manager) { 98 show(manager, DLG_ID); 99 } 100 101 @Override onResume()102 public void onResume() { 103 super.onResume(); 104 generateProgressAlertDialog(); 105 } 106 107 @Override onDestroy()108 public void onDestroy() { 109 super.onDestroy(); 110 if (mProgressAlertDialog != null && mProgressAlertDialog.isShowing()) { 111 mProgressAlertDialog.cancel(); 112 } 113 } 114 115 @Override getMetricsCategory()116 public int getMetricsCategory() { 117 return SettingsEnums.PROGRESS_DIALOG; 118 } 119 120 /** 121 * To generate a progress alter dialog and invoke the supported links dialog. 122 */ generateProgressAlertDialog()123 private void generateProgressAlertDialog() { 124 ThreadUtils.postOnBackgroundThread(() -> { 125 final long start = SystemClock.elapsedRealtime(); 126 queryLinksInBackground(); 127 IntentPickerUtils.logd( 128 "queryLinksInBackground take time: " + (SystemClock.elapsedRealtime() - start)); 129 if (mProgressAlertDialog.isShowing()) { 130 mHandle.post(() -> { 131 synchronized (mHandle) { 132 if (mProgressAlertDialog.isShowing()) { 133 mProgressAlertDialog.dismiss(); 134 IntentPickerUtils.logd("mProgressAlertDialog.dismiss() and isShowing: " 135 + mProgressAlertDialog.isShowing()); 136 launchSupportedLinksDialogFragment(); 137 } 138 } 139 }); 140 } 141 }); 142 } 143 queryLinksInBackground()144 private void queryLinksInBackground() { 145 final List<String> links = IntentPickerUtils.getLinksList(mDomainVerificationManager, 146 mPackage, DOMAIN_STATE_NONE); 147 final int linksNo = links.size(); 148 int index = 0; 149 mSupportedLinkWrapperList = new ArrayList<>(); 150 for (String host : links) { 151 final SortedSet<DomainOwner> ownerSet = 152 mDomainVerificationManager.getOwnersForDomain(host); 153 mSupportedLinkWrapperList.add(new SupportedLinkWrapper(getActivity(), host, ownerSet)); 154 index++; 155 // The cancel was clicked while progressing to collect data. 156 if (!mProgressAlertDialog.isShowing()) { 157 Log.w(TAG, "Exit the background thread!!!"); 158 // clear buffer 159 mSupportedLinkWrapperList.clear(); 160 break; 161 } 162 int progress = (int) (index * 100) / linksNo; 163 mHandle.post(() -> { 164 synchronized (mHandle) { 165 if (!mProgressAlertDialog.isShowing()) { 166 Log.w(TAG, "Exit the UI thread"); 167 return; 168 } 169 mProgressAlertDialog.getProgressBar().setProgress(progress); 170 } 171 }); 172 if (ownerSet.size() == 0) { 173 SystemClock.sleep(PROGRESS_BAR_STEPPING_TIME); 174 } 175 } 176 IntentPickerUtils.logd("queryLinksInBackground : SupportedLinkWrapperList size=" 177 + mSupportedLinkWrapperList.size()); 178 Collections.sort(mSupportedLinkWrapperList); 179 } 180 launchSupportedLinksDialogFragment()181 private void launchSupportedLinksDialogFragment() { 182 if (mSupportedLinkWrapperList.size() > 0) { 183 mViewModel.setSupportedLinkWrapperList(mSupportedLinkWrapperList); 184 final Bundle args = new Bundle(); 185 args.putString(AppLaunchSettings.APP_PACKAGE_KEY, mPackage); 186 final SupportedLinksDialogFragment dialogFragment = new SupportedLinksDialogFragment(); 187 dialogFragment.setArguments(args); 188 dialogFragment.showDialog(getActivity().getSupportFragmentManager()); 189 } 190 } 191 192 /** Create a custom {@link AlertDialog} with a {@link ProgressBar}. */ 193 static class ProgressAlertDialog extends AlertDialog { 194 private ProgressBar mProgressBar; 195 ProgressAlertDialog(@onNull Context context)196 protected ProgressAlertDialog(@NonNull Context context) { 197 this(context, 0); 198 } 199 ProgressAlertDialog(@onNull Context context, int themeResId)200 protected ProgressAlertDialog(@NonNull Context context, int themeResId) { 201 super(context, themeResId); 202 init(context); 203 } 204 init(Context context)205 private void init(Context context) { 206 final View view = LayoutInflater.from(context).inflate( 207 R.layout.app_launch_progress, /* root= */ null); 208 mProgressBar = view.findViewById(R.id.scan_links_progressbar); 209 mProgressBar.setProgress(0); 210 mProgressBar.setMax(100); 211 setView(view); 212 } 213 getProgressBar()214 ProgressBar getProgressBar() { 215 return mProgressBar; 216 } 217 } 218 } 219