| /* |
| * Copyright (C) 2026 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.settings.security; |
| |
| import android.app.Dialog; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.widget.EditText; |
| import android.widget.TextView; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.appcompat.app.AlertDialog; |
| import androidx.fragment.app.DialogFragment; |
| import androidx.fragment.app.Fragment; |
| |
| import com.android.settings.R; |
| |
| |
| /** |
| * Dialog for entering the current or new SIM card PIN. Used when the user wants to change |
| * the SIM card's PIN. |
| */ |
| public class EnterSimPinDialogFragment extends DialogFragment { |
| private static final String KEY_TITLE = "dialog_title"; |
| private static final String KEY_MESSAGE = "dialog_message"; |
| private static final String KEY_REMAINING_ATTEMPTS = "remaining_attempts"; |
| |
| private static final String TAG = "SimPinDialog"; |
| |
| public EnterSimPinDialogFragment() { |
| super(); |
| } |
| |
| public interface SimPinEntryListener { |
| /** |
| * Called when the user has entered a PIN and confirmed it. |
| * @param pin provided by the user. |
| */ |
| void onPinEntered(String pin); |
| |
| /** |
| * Called when the user has cancelled PIN entry. |
| */ |
| void onEntryCancelled(); |
| } |
| |
| private static EnterSimPinDialogFragment createFragmentWithArgs(int dialogTitleResource, |
| int dialogMessageResource) { |
| return createFragmentWithArgs(dialogTitleResource, |
| dialogMessageResource, /* attemptsRemaining= */0); |
| } |
| |
| private static EnterSimPinDialogFragment createFragmentWithArgs(int dialogTitleResource, |
| int dialogMessageResource, int attemptsRemaining) { |
| Bundle args = new Bundle(); |
| args.putInt(KEY_TITLE, dialogTitleResource); |
| args.putInt(KEY_MESSAGE, dialogMessageResource); |
| args.putInt(KEY_REMAINING_ATTEMPTS, attemptsRemaining); |
| |
| EnterSimPinDialogFragment fragment = new EnterSimPinDialogFragment(); |
| fragment.setArguments(args); |
| return fragment; |
| } |
| |
| /** |
| * Returns a dialog requesting the user to enter the current SIM PIN when the user starts |
| * the journey to protect the SIM with a PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newEnterCurrentPin() { |
| return createFragmentWithArgs(R.string.provide_current_sim_pin_title, |
| R.string.provide_current_sim_pin); |
| } |
| |
| /** |
| * Same as {@code newEnterCurrentPin} but with a hint about the correct format of the |
| * PIN. |
| * |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newEnterCurrentPinWithHint() { |
| return createFragmentWithArgs(R.string.provide_current_sim_pin_title, |
| R.string.sim_invalid_pin_hint); |
| } |
| |
| /** |
| * Returns a dialog requesting the user to enter the current SIM PIN when the user has already |
| * tried entering a PIN but the PIN provided is wrong. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newEnterCurrentPin(int numAttemptsRemaining) { |
| return createFragmentWithArgs(R.string.provide_current_sim_pin_title, |
| R.string.enter_current_sim_pin_after_mismatch, |
| numAttemptsRemaining); |
| } |
| |
| /** |
| * Returns a dialog asking the user to enter the new SIM PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newEnterNewPin() { |
| return createFragmentWithArgs(R.string.provide_new_sim_pin_title, |
| R.string.sim_enter_new); |
| } |
| |
| /** |
| * Same as {@code newEnterNewPin} but with a hint about the correct format of the |
| * PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newEnterNewPinWithHint() { |
| return createFragmentWithArgs(R.string.provide_new_sim_pin_title, |
| R.string.sim_invalid_pin_hint); |
| } |
| |
| /** |
| * Returns a dialog asking the user to confirm the new SIM PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newConfirmNewPin() { |
| return createFragmentWithArgs(R.string.confirm_new_sim_pin_title, |
| R.string.sim_reenter_new); |
| } |
| |
| /** |
| * Same as {@code newConfirmNewPin} but informing the user there is a mismatch between |
| * the two PINs they have provided as the new PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newConfirmNewPinAfterMismatch() { |
| return createFragmentWithArgs(R.string.provide_new_sim_pin_title, |
| R.string.confirm_new_sim_pin_after_mismatch); |
| } |
| |
| /** |
| * Same as {@code newConfirmNewPin} but with a hint about the correct format of the |
| * PIN. |
| * @return a dialog instance. |
| */ |
| static EnterSimPinDialogFragment newConfirmNewPinInstanceWithHint() { |
| return createFragmentWithArgs(R.string.confirm_new_sim_pin_title, |
| R.string.sim_invalid_pin_hint); |
| } |
| |
| @Override |
| public @NonNull Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { |
| //readValuesFromBundle(savedInstanceState); |
| Bundle args = requireArguments(); |
| int dialogTitleResource = args.getInt(KEY_TITLE); |
| int mDialogMessageResource = args.getInt(KEY_MESSAGE); |
| int mAttemptsRemaining = args.getInt(KEY_REMAINING_ATTEMPTS); |
| |
| |
| AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()) |
| .setTitle(dialogTitleResource) |
| .setCancelable(true) |
| .setPositiveButton(R.string.sim_enter_ok, |
| (dialog, which) -> invokePinEnteredCallback()) |
| .setNegativeButton(com.android.internal.R.string.cancel, |
| (dialog, which) -> invokeCancelCallback()); |
| |
| if (mAttemptsRemaining == 0) { |
| builder.setMessage(mDialogMessageResource); |
| } else { |
| builder.setMessage(getContext().getResources().getString(mDialogMessageResource, |
| mAttemptsRemaining)); |
| } |
| |
| builder.setView(R.layout.dialog_provide_sim_pin_entry); |
| return builder.create(); |
| } |
| |
| @Nullable |
| private SimPinEntryListener getPinEntryListener() { |
| Fragment parent = getParentFragment(); |
| if (parent == null || !(parent instanceof SimPinEntryListener)) { |
| Log.w(TAG, "No or wrong parent fragment, entered PIN will have no impact."); |
| return null; |
| } |
| |
| return (SimPinEntryListener) parent; |
| } |
| |
| private void invokePinEnteredCallback() { |
| EditText pinInput = getDialog().findViewById(R.id.current_sim_pin); |
| |
| String pin = ((TextView) pinInput).getText().toString(); |
| dismiss(); |
| SimPinEntryListener listener = getPinEntryListener(); |
| if (listener != null) { |
| listener.onPinEntered(pin); |
| } |
| } |
| |
| private void invokeCancelCallback() { |
| dismiss(); |
| SimPinEntryListener listener = getPinEntryListener(); |
| if (listener != null) { |
| listener.onEntryCancelled(); |
| } |
| } |
| } |