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