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.packageinstaller; 18 19 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 20 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.app.Dialog; 24 import android.app.DialogFragment; 25 import android.app.PendingIntent; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.IntentSender; 29 import android.content.pm.PackageInstaller; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.provider.Settings; 33 import android.text.format.Formatter; 34 import android.util.Log; 35 36 import androidx.annotation.Nullable; 37 38 public class UnarchiveErrorFragment extends DialogFragment implements 39 DialogInterface.OnClickListener { 40 41 private static final String TAG = "UnarchiveErrorFragment"; 42 43 private int mStatus; 44 45 @Nullable 46 private PendingIntent mExtraIntent; 47 48 @Nullable 49 private String mInstallerPackageName; 50 51 @Override onCreateDialog(Bundle savedInstanceState)52 public Dialog onCreateDialog(Bundle savedInstanceState) { 53 Bundle args = getArguments(); 54 mStatus = args.getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, -1); 55 mExtraIntent = args.getParcelable(Intent.EXTRA_INTENT, PendingIntent.class); 56 long requiredBytes = args.getLong(UnarchiveErrorActivity.EXTRA_REQUIRED_BYTES); 57 mInstallerPackageName = args.getString( 58 UnarchiveErrorActivity.EXTRA_INSTALLER_PACKAGE_NAME); 59 String installerAppTitle = args.getString(UnarchiveErrorActivity.EXTRA_INSTALLER_TITLE); 60 61 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); 62 63 dialogBuilder.setTitle(getDialogTitle(mStatus, installerAppTitle)); 64 dialogBuilder.setMessage(getBodyText(mStatus, installerAppTitle, requiredBytes)); 65 66 addButtons(dialogBuilder, mStatus); 67 68 return dialogBuilder.create(); 69 } 70 addButtons(AlertDialog.Builder dialogBuilder, int status)71 private void addButtons(AlertDialog.Builder dialogBuilder, int status) { 72 switch (status) { 73 case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: 74 dialogBuilder.setPositiveButton(R.string.unarchive_action_required_continue, this); 75 dialogBuilder.setNegativeButton(R.string.close, this); 76 break; 77 case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: 78 dialogBuilder.setPositiveButton(R.string.unarchive_clear_storage_button, this); 79 dialogBuilder.setNegativeButton(R.string.close, this); 80 break; 81 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: 82 dialogBuilder.setPositiveButton(R.string.external_sources_settings, this); 83 dialogBuilder.setNegativeButton(R.string.close, this); 84 break; 85 case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: 86 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: 87 case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: 88 dialogBuilder.setPositiveButton(android.R.string.ok, this); 89 break; 90 default: 91 // This should never happen through normal API usage. 92 throw new IllegalArgumentException("Invalid unarchive status " + status); 93 } 94 } 95 getBodyText(int status, String installerAppTitle, long requiredBytes)96 private String getBodyText(int status, String installerAppTitle, long requiredBytes) { 97 switch (status) { 98 case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: 99 return getString(R.string.unarchive_action_required_body); 100 case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: 101 return String.format(getString(R.string.unarchive_error_storage_body), 102 Formatter.formatShortFileSize(getActivity(), requiredBytes)); 103 case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: 104 return getString(R.string.unarchive_error_offline_body); 105 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: 106 return String.format(getString(R.string.unarchive_error_installer_disabled_body), 107 installerAppTitle); 108 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: 109 return String.format( 110 getString(R.string.unarchive_error_installer_uninstalled_body), 111 installerAppTitle); 112 case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: 113 return getString(R.string.unarchive_error_generic_body); 114 default: 115 // This should never happen through normal API usage. 116 throw new IllegalArgumentException("Invalid unarchive status " + status); 117 } 118 } 119 getDialogTitle(int status, String installerAppTitle)120 private String getDialogTitle(int status, String installerAppTitle) { 121 switch (status) { 122 case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: 123 return getString(R.string.unarchive_action_required_title); 124 case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: 125 return getString(R.string.unarchive_error_storage_title); 126 case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: 127 return getString(R.string.unarchive_error_offline_title); 128 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: 129 return String.format(getString(R.string.unarchive_error_installer_disabled_title), 130 installerAppTitle); 131 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: 132 return String.format( 133 getString(R.string.unarchive_error_installer_uninstalled_title), 134 installerAppTitle); 135 case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: 136 return getString(R.string.unarchive_error_generic_title); 137 default: 138 // This should never happen through normal API usage. 139 throw new IllegalArgumentException("Invalid unarchive status " + status); 140 } 141 } 142 143 @Override onClick(DialogInterface dialog, int which)144 public void onClick(DialogInterface dialog, int which) { 145 if (which != Dialog.BUTTON_POSITIVE) { 146 return; 147 } 148 149 try { 150 onClickInternal(); 151 } catch (IntentSender.SendIntentException e) { 152 Log.e(TAG, "Failed to start intent after onClick.", e); 153 } 154 } 155 onClickInternal()156 private void onClickInternal() throws IntentSender.SendIntentException { 157 Activity activity = getActivity(); 158 if (activity == null) { 159 // This probably shouldn't happen in practice. 160 Log.i(TAG, "Lost reference to activity, cannot act onClick."); 161 return; 162 } 163 164 switch (mStatus) { 165 case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: 166 activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */ 167 null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0); 168 break; 169 case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: 170 if (mExtraIntent != null) { 171 activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */ 172 null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0); 173 } else { 174 Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE"); 175 startActivity(intent); 176 } 177 break; 178 case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: 179 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 180 Uri uri = Uri.fromParts("package", mInstallerPackageName, null); 181 intent.setData(uri); 182 startActivity(intent); 183 break; 184 default: 185 // Do nothing. The rest of the dialogs are purely informational. 186 } 187 } 188 189 @Override onDismiss(DialogInterface dialog)190 public void onDismiss(DialogInterface dialog) { 191 super.onDismiss(dialog); 192 if (isAdded()) { 193 getActivity().finish(); 194 } 195 } 196 } 197