/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.packageinstaller; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.text.format.Formatter; import android.util.Log; import androidx.annotation.Nullable; public class UnarchiveErrorFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String TAG = "UnarchiveErrorFragment"; private int mStatus; @Nullable private PendingIntent mExtraIntent; @Nullable private String mInstallerPackageName; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle args = getArguments(); mStatus = args.getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, -1); mExtraIntent = args.getParcelable(Intent.EXTRA_INTENT, PendingIntent.class); long requiredBytes = args.getLong(UnarchiveErrorActivity.EXTRA_REQUIRED_BYTES); mInstallerPackageName = args.getString( UnarchiveErrorActivity.EXTRA_INSTALLER_PACKAGE_NAME); String installerAppTitle = args.getString(UnarchiveErrorActivity.EXTRA_INSTALLER_TITLE); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle(getDialogTitle(mStatus, installerAppTitle)); dialogBuilder.setMessage(getBodyText(mStatus, installerAppTitle, requiredBytes)); addButtons(dialogBuilder, mStatus); return dialogBuilder.create(); } private void addButtons(AlertDialog.Builder dialogBuilder, int status) { switch (status) { case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: dialogBuilder.setPositiveButton(R.string.unarchive_action_required_continue, this); dialogBuilder.setNegativeButton(R.string.close, this); break; case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: dialogBuilder.setPositiveButton(R.string.unarchive_clear_storage_button, this); dialogBuilder.setNegativeButton(R.string.close, this); break; case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: dialogBuilder.setPositiveButton(R.string.external_sources_settings, this); dialogBuilder.setNegativeButton(R.string.close, this); break; case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: dialogBuilder.setPositiveButton(android.R.string.ok, this); break; default: // This should never happen through normal API usage. throw new IllegalArgumentException("Invalid unarchive status " + status); } } private String getBodyText(int status, String installerAppTitle, long requiredBytes) { switch (status) { case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: return getString(R.string.unarchive_action_required_body); case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: return String.format(getString(R.string.unarchive_error_storage_body), Formatter.formatShortFileSize(getActivity(), requiredBytes)); case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: return getString(R.string.unarchive_error_offline_body); case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: return String.format(getString(R.string.unarchive_error_installer_disabled_body), installerAppTitle); case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: return String.format( getString(R.string.unarchive_error_installer_uninstalled_body), installerAppTitle); case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: return getString(R.string.unarchive_error_generic_body); default: // This should never happen through normal API usage. throw new IllegalArgumentException("Invalid unarchive status " + status); } } private String getDialogTitle(int status, String installerAppTitle) { switch (status) { case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: return getString(R.string.unarchive_action_required_title); case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: return getString(R.string.unarchive_error_storage_title); case PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY: return getString(R.string.unarchive_error_offline_title); case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: return String.format(getString(R.string.unarchive_error_installer_disabled_title), installerAppTitle); case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED: return String.format( getString(R.string.unarchive_error_installer_uninstalled_title), installerAppTitle); case PackageInstaller.UNARCHIVAL_GENERIC_ERROR: return getString(R.string.unarchive_error_generic_title); default: // This should never happen through normal API usage. throw new IllegalArgumentException("Invalid unarchive status " + status); } } @Override public void onClick(DialogInterface dialog, int which) { if (which != Dialog.BUTTON_POSITIVE) { return; } try { onClickInternal(); } catch (IntentSender.SendIntentException e) { Log.e(TAG, "Failed to start intent after onClick.", e); } } private void onClickInternal() throws IntentSender.SendIntentException { Activity activity = getActivity(); if (activity == null) { // This probably shouldn't happen in practice. Log.i(TAG, "Lost reference to activity, cannot act onClick."); return; } switch (mStatus) { case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED: activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */ null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0); break; case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE: if (mExtraIntent != null) { activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */ null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0); } else { Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE"); startActivity(intent); } break; case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED: Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", mInstallerPackageName, null); intent.setData(uri); startActivity(intent); break; default: // Do nothing. The rest of the dialogs are purely informational. } } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); if (isAdded()) { getActivity().finish(); } } }