am 71030e53: Merge "Add SIM PUK unlockscreen." into honeycomb-LTE
* commit '71030e536522449f34dc584fda94f34e7be6ee19': Add SIM PUK unlockscreen.
This commit is contained in:
@@ -33,6 +33,7 @@ import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import java.io.File;
|
||||
@@ -775,6 +776,16 @@ public class LockPatternUtils {
|
||||
setBoolean(LOCKOUT_PERMANENT_KEY, locked);
|
||||
}
|
||||
|
||||
public boolean isEmergencyCallCapable() {
|
||||
return mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_voice_capable);
|
||||
}
|
||||
|
||||
public boolean isPukUnlockScreenEnable() {
|
||||
return mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_enable_puk_unlock_screen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A formatted string of the next alarm (for showing on the lock screen),
|
||||
* or null if there is no next alarm.
|
||||
@@ -827,11 +838,22 @@ public class LockPatternUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text on the emergency button to indicate what action will be taken.
|
||||
* Sets the emergency button visibility based on isEmergencyCallCapable().
|
||||
*
|
||||
* If the emergency button is visible, sets the text on the emergency button
|
||||
* to indicate what action will be taken.
|
||||
*
|
||||
* If there's currently a call in progress, the button will take them to the call
|
||||
* @param button the button to update
|
||||
*/
|
||||
public void updateEmergencyCallButtonState(Button button) {
|
||||
if (isEmergencyCallCapable()) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
int newState = TelephonyManager.getDefault().getCallState();
|
||||
int textId;
|
||||
if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
|
||||
|
||||
182
core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
Normal file
182
core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
-->
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/background_dark"
|
||||
>
|
||||
|
||||
<LinearLayout android:id="@+id/topDisplayGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- header text ('Enter Puk Code') -->
|
||||
<TextView android:id="@+id/headerText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||
|
||||
<!-- Carrier info -->
|
||||
<TextView android:id="@+id/carrier"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="9dip"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="10dip"
|
||||
android:layout_marginLeft="10dip">
|
||||
<TextView android:id="@+id/enter_puk"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@android:string/keyguard_password_enter_puk_prompt"
|
||||
android:textSize="30sp"
|
||||
android:layout_marginBottom="10dip"/>
|
||||
<TextView android:id="@+id/enter_pin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@android:string/keyguard_password_enter_pin_prompt"
|
||||
android:textSize="30sp"
|
||||
android:layout_marginTop="10dip"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="0dip"
|
||||
android:layout_marginRight="10dip"
|
||||
android:layout_marginLeft="10dip">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@android:drawable/edit_text">
|
||||
|
||||
<!-- displays dots as user enters puk -->
|
||||
<TextView android:id="@+id/pukDisplay"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLargeInverse"
|
||||
android:textStyle="bold"
|
||||
android:inputType="textPassword"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/pukDel"
|
||||
android:src="@android:drawable/ic_input_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="-3dip"
|
||||
android:layout_marginBottom="-3dip"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@android:drawable/edit_text">
|
||||
|
||||
<!-- displays dots as user enters new pin -->
|
||||
<TextView android:id="@+id/pinDisplay"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLargeInverse"
|
||||
android:textStyle="bold"
|
||||
android:inputType="textPassword"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/pinDel"
|
||||
android:src="@android:drawable/ic_input_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="-3dip"
|
||||
android:layout_marginBottom="-3dip"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:layout_marginRight="8dip">
|
||||
|
||||
<Button android:id="@+id/ok"
|
||||
android:text="@android:string/ok"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginRight="8dip"
|
||||
android:textSize="18sp"
|
||||
/>
|
||||
|
||||
<Button android:id="@+id/emergencyCall"
|
||||
android:text="@android:string/lockscreen_emergency_call"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginLeft="8dip"
|
||||
android:textSize="18sp"
|
||||
android:drawableLeft="@drawable/ic_emergency"
|
||||
android:drawablePadding="8dip"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
185
core/res/res/layout/keyguard_screen_sim_puk_portrait.xml
Normal file
185
core/res/res/layout/keyguard_screen_sim_puk_portrait.xml
Normal file
@@ -0,0 +1,185 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
**
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/background_dark"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<LinearLayout android:id="@+id/topDisplayGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- header text ('Enter Puk Code') -->
|
||||
<TextView android:id="@+id/headerText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"/>
|
||||
|
||||
<!-- Carrier info -->
|
||||
<TextView android:id="@+id/carrier"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="9dip"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="10dip"
|
||||
android:layout_marginLeft="10dip">
|
||||
<TextView android:id="@+id/enter_puk"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@android:string/keyguard_password_enter_puk_prompt"
|
||||
android:textSize="30sp"
|
||||
android:layout_marginBottom="10dip"/>
|
||||
<TextView android:id="@+id/enter_pin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@android:string/keyguard_password_enter_pin_prompt"
|
||||
android:textSize="30sp"
|
||||
android:layout_marginTop="10dip"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingRight="0dip"
|
||||
android:layout_marginRight="10dip"
|
||||
android:layout_marginLeft="10dip">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@android:drawable/edit_text">
|
||||
|
||||
<!-- displays dots as user enters puk -->
|
||||
<TextView android:id="@+id/pukDisplay"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLargeInverse"
|
||||
android:textStyle="bold"
|
||||
android:inputType="textPassword"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/pukDel"
|
||||
android:src="@android:drawable/ic_input_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="-3dip"
|
||||
android:layout_marginBottom="-3dip"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginRight="6dip"
|
||||
android:layout_marginLeft="6dip"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@android:drawable/edit_text">
|
||||
|
||||
<!-- displays dots as user enters new pin -->
|
||||
<TextView android:id="@+id/pinDisplay"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLargeInverse"
|
||||
android:textStyle="bold"
|
||||
android:inputType="textPassword"
|
||||
/>
|
||||
|
||||
<ImageButton android:id="@+id/pinDel"
|
||||
android:src="@android:drawable/ic_input_delete"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="-3dip"
|
||||
android:layout_marginBottom="-3dip"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/keyPad"
|
||||
layout="@android:layout/twelve_key_entry"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/topDisplayGroup"
|
||||
android:layout_marginTop="10dip"
|
||||
/>
|
||||
|
||||
<!-- spacer below keypad -->
|
||||
<View
|
||||
android:id="@+id/spacerBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dip"
|
||||
android:layout_marginTop="6dip"
|
||||
android:layout_above="@id/emergencyCall"
|
||||
android:background="@android:drawable/divider_horizontal_dark"
|
||||
/>
|
||||
|
||||
<!-- The emergency button should take the rest of the space and be centered vertically -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- emergency call button -->
|
||||
<Button
|
||||
android:id="@+id/emergencyCall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@android:drawable/ic_emergency"
|
||||
android:drawablePadding="8dip"
|
||||
android:text="@android:string/lockscreen_emergency_call"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -370,6 +370,10 @@
|
||||
<!-- Diable lockscreen rotation by default -->
|
||||
<bool name="config_enableLockScreenRotation">false</bool>
|
||||
|
||||
<!-- Diable puk unlockscreen by default.
|
||||
If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
|
||||
<bool name="config_enable_puk_unlock_screen">false</bool>
|
||||
|
||||
<!-- Control the behavior when the user long presses the power button.
|
||||
0 - Nothing
|
||||
1 - Recent apps dialog
|
||||
|
||||
@@ -87,6 +87,8 @@
|
||||
<string name="mismatchPin">The PINs you entered do not match.</string>
|
||||
<!-- Displayed when a SIM PIN password is too long or too short. -->
|
||||
<string name="invalidPin">Type a PIN that is 4 to 8 numbers.</string>
|
||||
<!-- Displayed when a SIM PUK password is too short. -->
|
||||
<string name="invalidPuk">Type a PUK that is 8 numbers or longer.</string>
|
||||
<!-- Displayed to prompt the user to type the PUK password to unlock
|
||||
the SIM card. -->
|
||||
<string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
|
||||
@@ -1644,6 +1646,15 @@
|
||||
Displayed in one line in a large font. -->
|
||||
<string name="keyguard_password_enter_pin_code">Enter PIN code</string>
|
||||
|
||||
<!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
|
||||
Displayed in one line in a large font. -->
|
||||
<string name="keyguard_password_enter_puk_code">Enter PUK and new PIN code</string>
|
||||
|
||||
<!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
|
||||
<string name="keyguard_password_enter_puk_prompt">PUK code</string>
|
||||
<!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
|
||||
<string name="keyguard_password_enter_pin_prompt">New Pin Code</string>
|
||||
|
||||
<!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
|
||||
<string name="keyguard_password_entry_touch_hint"><font size="17">Touch to enter password</font></string>
|
||||
|
||||
|
||||
@@ -480,11 +480,11 @@ public class KeyguardUpdateMonitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Report that the user succesfully entered the sim pin so we
|
||||
* Report that the user succesfully entered the sim pin or puk so we
|
||||
* have the information earlier than waiting for the intent
|
||||
* broadcast from the telephony code.
|
||||
*/
|
||||
public void reportSimPinUnlocked() {
|
||||
public void reportSimUnlocked() {
|
||||
mSimState = IccCard.State.READY;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
*/
|
||||
SimPin,
|
||||
|
||||
/**
|
||||
* Unlock by entering a sim puk.
|
||||
*/
|
||||
SimPuk,
|
||||
|
||||
/**
|
||||
* Unlock by entering an account's login and password.
|
||||
*/
|
||||
@@ -222,8 +227,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
public void goToUnlockScreen() {
|
||||
final IccCard.State simState = mUpdateMonitor.getSimState();
|
||||
if (stuckOnLockScreenBecauseSimMissing()
|
||||
|| (simState == IccCard.State.PUK_REQUIRED)){
|
||||
// stuck on lock screen when sim missing or puk'd
|
||||
|| (simState == IccCard.State.PUK_REQUIRED
|
||||
&& !mLockPatternUtils.isPukUnlockScreenEnable())){
|
||||
// stuck on lock screen when sim missing or
|
||||
// puk'd but puk unlock screen is disabled
|
||||
return;
|
||||
}
|
||||
if (!isSecure()) {
|
||||
@@ -522,8 +529,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
secure = mLockPatternUtils.isLockPatternEnabled();
|
||||
break;
|
||||
case SimPin:
|
||||
secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
|
||||
|| mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
|
||||
secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
|
||||
break;
|
||||
case SimPuk:
|
||||
secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
|
||||
break;
|
||||
case Account:
|
||||
secure = true;
|
||||
@@ -592,6 +601,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
|
||||
View createUnlockScreenFor(UnlockMode unlockMode) {
|
||||
View unlockView = null;
|
||||
|
||||
if (DEBUG) Log.d(TAG,
|
||||
"createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
|
||||
|
||||
if (unlockMode == UnlockMode.Pattern) {
|
||||
PatternUnlockScreen view = new PatternUnlockScreen(
|
||||
mContext,
|
||||
@@ -600,10 +613,15 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
mUpdateMonitor,
|
||||
mKeyguardScreenCallback,
|
||||
mUpdateMonitor.getFailedAttempts());
|
||||
if (DEBUG) Log.d(TAG,
|
||||
"createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
|
||||
view.setEnableFallback(mEnableFallback);
|
||||
unlockView = view;
|
||||
} else if (unlockMode == UnlockMode.SimPuk) {
|
||||
unlockView = new SimPukUnlockScreen(
|
||||
mContext,
|
||||
mConfiguration,
|
||||
mUpdateMonitor,
|
||||
mKeyguardScreenCallback,
|
||||
mLockPatternUtils);
|
||||
} else if (unlockMode == UnlockMode.SimPin) {
|
||||
unlockView = new SimUnlockScreen(
|
||||
mContext,
|
||||
@@ -654,7 +672,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
*/
|
||||
private Mode getInitialMode() {
|
||||
final IccCard.State simState = mUpdateMonitor.getSimState();
|
||||
if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) {
|
||||
if (stuckOnLockScreenBecauseSimMissing() ||
|
||||
(simState == IccCard.State.PUK_REQUIRED &&
|
||||
!mLockPatternUtils.isPukUnlockScreenEnable())) {
|
||||
return Mode.LockScreen;
|
||||
} else {
|
||||
// Show LockScreen first for any screen other than Pattern unlock.
|
||||
@@ -676,8 +696,10 @@ public class LockPatternKeyguardView extends KeyguardViewBase {
|
||||
private UnlockMode getUnlockMode() {
|
||||
final IccCard.State simState = mUpdateMonitor.getSimState();
|
||||
UnlockMode currentMode;
|
||||
if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) {
|
||||
if (simState == IccCard.State.PIN_REQUIRED) {
|
||||
currentMode = UnlockMode.SimPin;
|
||||
} else if (simState == IccCard.State.PUK_REQUIRED) {
|
||||
currentMode = UnlockMode.SimPuk;
|
||||
} else {
|
||||
final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
|
||||
switch (mode) {
|
||||
|
||||
@@ -232,7 +232,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void onGrabbedStateChange(View v, int grabbedState) {
|
||||
if (DBG) Log.v(TAG, "*** LockScreen accel is "
|
||||
if (DBG) Log.v(TAG, "*** LockScreen accel is "
|
||||
+ (mEnergyWave.isHardwareAccelerated() ? "on":"off"));
|
||||
// Don't poke the wake lock when returning to a state where the handle is
|
||||
// not grabbed since that can happen when the system (instead of the user)
|
||||
@@ -579,10 +579,16 @@ class LockScreen extends LinearLayout implements KeyguardScreen,
|
||||
mScreenLocked.setText(R.string.lockscreen_sim_puk_locked_instructions);
|
||||
|
||||
// layout
|
||||
mScreenLocked.setVisibility(View.VISIBLE);
|
||||
mEmergencyCallText.setVisibility(View.VISIBLE);
|
||||
mEmergencyCallButton.setVisibility(View.VISIBLE);
|
||||
disableUnlock();
|
||||
if (mLockPatternUtils.isPukUnlockScreenEnable()) {
|
||||
mScreenLocked.setVisibility(View.INVISIBLE);
|
||||
mEmergencyCallText.setVisibility(View.GONE);
|
||||
enableUnlock();
|
||||
} else {
|
||||
mScreenLocked.setVisibility(View.VISIBLE);
|
||||
mEmergencyCallText.setVisibility(View.VISIBLE);
|
||||
mEmergencyCallButton.setVisibility(View.VISIBLE);
|
||||
disableUnlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.internal.policy.impl;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import com.android.internal.telephony.ITelephony;
|
||||
import com.android.internal.telephony.IccCard;
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* Displays a dialer like interface to unlock the SIM PUK.
|
||||
*/
|
||||
public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen,
|
||||
View.OnClickListener, KeyguardUpdateMonitor.InfoCallback {
|
||||
|
||||
private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
|
||||
|
||||
private final KeyguardUpdateMonitor mUpdateMonitor;
|
||||
private final KeyguardScreenCallback mCallback;
|
||||
|
||||
private TextView mHeaderText;
|
||||
private TextView mPukText;
|
||||
private TextView mPinText;
|
||||
|
||||
private TextView mFocusedEntry;
|
||||
|
||||
private TextView mOkButton;
|
||||
private Button mEmergencyCallButton;
|
||||
|
||||
private View mDelPukButton;
|
||||
private View mDelPinButton;
|
||||
|
||||
private ProgressDialog mSimUnlockProgressDialog = null;
|
||||
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
|
||||
private int mCreationOrientation;
|
||||
|
||||
private int mKeyboardHidden;
|
||||
|
||||
private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
|
||||
public SimPukUnlockScreen(Context context, Configuration configuration,
|
||||
KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
|
||||
LockPatternUtils lockpatternutils) {
|
||||
super(context);
|
||||
mUpdateMonitor = updateMonitor;
|
||||
mCallback = callback;;
|
||||
|
||||
mCreationOrientation = configuration.orientation;
|
||||
mKeyboardHidden = configuration.hardKeyboardHidden;
|
||||
mLockPatternUtils = lockpatternutils;
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
|
||||
inflater.inflate(
|
||||
R.layout.keyguard_screen_sim_puk_landscape, this, true);
|
||||
} else {
|
||||
inflater.inflate(
|
||||
R.layout.keyguard_screen_sim_puk_portrait, this, true);
|
||||
new TouchInput();
|
||||
}
|
||||
|
||||
mHeaderText = (TextView) findViewById(R.id.headerText);
|
||||
mPukText = (TextView) findViewById(R.id.pukDisplay);
|
||||
mPukText.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
requestFocus(mPukText);
|
||||
mCallback.pokeWakelock();
|
||||
}
|
||||
});
|
||||
mPinText = (TextView) findViewById(R.id.pinDisplay);
|
||||
mPinText.setOnClickListener(this);
|
||||
|
||||
mDelPukButton = findViewById(R.id.pukDel);
|
||||
mDelPukButton.setOnClickListener(this);
|
||||
mDelPinButton = findViewById(R.id.pinDel);
|
||||
mDelPinButton.setOnClickListener(this);
|
||||
|
||||
|
||||
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
|
||||
mOkButton = (TextView) findViewById(R.id.ok);
|
||||
|
||||
mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
|
||||
mPukText.setFocusable(false);
|
||||
mPinText.setFocusable(false);
|
||||
mOkButton.setOnClickListener(this);
|
||||
|
||||
requestFocus(mPukText);
|
||||
|
||||
if (mLockPatternUtils.isEmergencyCallCapable()) {
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
mEmergencyCallButton.setOnClickListener(this);
|
||||
} else {
|
||||
mEmergencyCallButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
private void requestFocus(TextView entry) {
|
||||
mFocusedEntry = entry;
|
||||
mFocusedEntry.setText("");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public boolean needsInput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void onPause() {
|
||||
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void onResume() {
|
||||
// start fresh
|
||||
mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
|
||||
requestFocus(mPukText);
|
||||
mPinText.setText("");
|
||||
|
||||
if (mLockPatternUtils.isEmergencyCallCapable()) {
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void cleanUp() {
|
||||
// dismiss the dialog.
|
||||
if (mSimUnlockProgressDialog != null) {
|
||||
mSimUnlockProgressDialog.dismiss();
|
||||
mSimUnlockProgressDialog = null;
|
||||
}
|
||||
mUpdateMonitor.removeCallback(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Since the IPC can block, we want to run the request in a separate thread
|
||||
* with a callback.
|
||||
*/
|
||||
private abstract class CheckSimPuk extends Thread {
|
||||
|
||||
private final String mPin, mPuk;
|
||||
|
||||
protected CheckSimPuk(String puk, String pin) {
|
||||
mPuk = puk;
|
||||
mPin = pin;
|
||||
}
|
||||
|
||||
abstract void onSimLockChangedResponse(boolean success);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final boolean result = ITelephony.Stub.asInterface(ServiceManager
|
||||
.checkService("phone")).supplyPuk(mPuk, mPin);
|
||||
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
onSimLockChangedResponse(result);
|
||||
}
|
||||
});
|
||||
} catch (RemoteException e) {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
onSimLockChangedResponse(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v == mDelPukButton) {
|
||||
final Editable digits = mPukText.getEditableText();
|
||||
final int len = digits.length();
|
||||
if (len > 0) {
|
||||
digits.delete(len-1, len);
|
||||
}
|
||||
mCallback.pokeWakelock();
|
||||
} else if (v == mDelPinButton) {
|
||||
final Editable digits = mPinText.getEditableText();
|
||||
final int len = digits.length();
|
||||
if (len > 0) {
|
||||
digits.delete(len-1, len);
|
||||
}
|
||||
mCallback.pokeWakelock();
|
||||
} else if (v == mPinText) {
|
||||
requestFocus(mPinText);
|
||||
mCallback.pokeWakelock();
|
||||
} else if (v == mEmergencyCallButton) {
|
||||
mCallback.takeEmergencyCallAction();
|
||||
} else if (v == mOkButton) {
|
||||
checkPuk();
|
||||
}
|
||||
}
|
||||
|
||||
private Dialog getSimUnlockProgressDialog() {
|
||||
if (mSimUnlockProgressDialog == null) {
|
||||
mSimUnlockProgressDialog = new ProgressDialog(mContext);
|
||||
mSimUnlockProgressDialog.setMessage(
|
||||
mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message));
|
||||
mSimUnlockProgressDialog.setIndeterminate(true);
|
||||
mSimUnlockProgressDialog.setCancelable(false);
|
||||
mSimUnlockProgressDialog.getWindow().setType(
|
||||
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
|
||||
if (!mContext.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_sf_slowBlur)) {
|
||||
mSimUnlockProgressDialog.getWindow().setFlags(
|
||||
WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
|
||||
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
|
||||
}
|
||||
}
|
||||
return mSimUnlockProgressDialog;
|
||||
}
|
||||
|
||||
private void checkPuk() {
|
||||
// make sure that the puk is at least 8 digits long.
|
||||
if (mPukText.getText().length() < 8) {
|
||||
// otherwise, display a message to the user, and don't submit.
|
||||
mHeaderText.setText(R.string.invalidPuk);
|
||||
mPukText.setText("");
|
||||
mCallback.pokeWakelock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPinText.getText().length() < 4
|
||||
|| mPinText.getText().length() > 8) {
|
||||
// otherwise, display a message to the user, and don't submit.
|
||||
mHeaderText.setText(R.string.invalidPin);
|
||||
mPinText.setText("");
|
||||
mCallback.pokeWakelock();
|
||||
return;
|
||||
}
|
||||
|
||||
getSimUnlockProgressDialog().show();
|
||||
|
||||
new CheckSimPuk(mPukText.getText().toString(),
|
||||
mPinText.getText().toString()) {
|
||||
void onSimLockChangedResponse(boolean success) {
|
||||
if (mSimUnlockProgressDialog != null) {
|
||||
mSimUnlockProgressDialog.hide();
|
||||
}
|
||||
if (success) {
|
||||
// before closing the keyguard, report back that
|
||||
// the sim is unlocked so it knows right away
|
||||
mUpdateMonitor.reportSimUnlocked();
|
||||
mCallback.goToUnlockScreen();
|
||||
} else {
|
||||
mHeaderText.setText(R.string.badPuk);
|
||||
mPukText.setText("");
|
||||
mPinText.setText("");
|
||||
}
|
||||
mCallback.pokeWakelock();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
mCallback.goToLockScreen();
|
||||
return true;
|
||||
}
|
||||
final char match = event.getMatch(DIGITS);
|
||||
if (match != 0) {
|
||||
reportDigit(match - '0');
|
||||
return true;
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_DEL) {
|
||||
mFocusedEntry.onKeyDown(keyCode, event);
|
||||
final Editable digits = mFocusedEntry.getEditableText();
|
||||
final int len = digits.length();
|
||||
if (len > 0) {
|
||||
digits.delete(len-1, len);
|
||||
}
|
||||
mCallback.pokeWakelock();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
checkPuk();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reportDigit(int digit) {
|
||||
mFocusedEntry.append(Integer.toString(digit));
|
||||
}
|
||||
|
||||
void updateConfiguration() {
|
||||
Configuration newConfig = getResources().getConfiguration();
|
||||
if (newConfig.orientation != mCreationOrientation) {
|
||||
mCallback.recreateMe(newConfig);
|
||||
} else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
|
||||
mKeyboardHidden = newConfig.hardKeyboardHidden;
|
||||
final boolean isKeyboardOpen =
|
||||
(mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
|
||||
if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
|
||||
mCallback.goToUnlockScreen();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
updateConfiguration();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to handle input from touch dialer. Only relevant when
|
||||
* the keyboard is shut.
|
||||
*/
|
||||
private class TouchInput implements View.OnClickListener {
|
||||
private TextView mZero;
|
||||
private TextView mOne;
|
||||
private TextView mTwo;
|
||||
private TextView mThree;
|
||||
private TextView mFour;
|
||||
private TextView mFive;
|
||||
private TextView mSix;
|
||||
private TextView mSeven;
|
||||
private TextView mEight;
|
||||
private TextView mNine;
|
||||
private TextView mCancelButton;
|
||||
|
||||
private TouchInput() {
|
||||
mZero = (TextView) findViewById(R.id.zero);
|
||||
mOne = (TextView) findViewById(R.id.one);
|
||||
mTwo = (TextView) findViewById(R.id.two);
|
||||
mThree = (TextView) findViewById(R.id.three);
|
||||
mFour = (TextView) findViewById(R.id.four);
|
||||
mFive = (TextView) findViewById(R.id.five);
|
||||
mSix = (TextView) findViewById(R.id.six);
|
||||
mSeven = (TextView) findViewById(R.id.seven);
|
||||
mEight = (TextView) findViewById(R.id.eight);
|
||||
mNine = (TextView) findViewById(R.id.nine);
|
||||
mCancelButton = (TextView) findViewById(R.id.cancel);
|
||||
|
||||
mZero.setText("0");
|
||||
mOne.setText("1");
|
||||
mTwo.setText("2");
|
||||
mThree.setText("3");
|
||||
mFour.setText("4");
|
||||
mFive.setText("5");
|
||||
mSix.setText("6");
|
||||
mSeven.setText("7");
|
||||
mEight.setText("8");
|
||||
mNine.setText("9");
|
||||
|
||||
mZero.setOnClickListener(this);
|
||||
mOne.setOnClickListener(this);
|
||||
mTwo.setOnClickListener(this);
|
||||
mThree.setOnClickListener(this);
|
||||
mFour.setOnClickListener(this);
|
||||
mFive.setOnClickListener(this);
|
||||
mSix.setOnClickListener(this);
|
||||
mSeven.setOnClickListener(this);
|
||||
mEight.setOnClickListener(this);
|
||||
mNine.setOnClickListener(this);
|
||||
mCancelButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
public void onClick(View v) {
|
||||
if (v == mCancelButton) {
|
||||
mCallback.goToLockScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
final int digit = checkDigit(v);
|
||||
if (digit >= 0) {
|
||||
mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
|
||||
reportDigit(digit);
|
||||
}
|
||||
}
|
||||
|
||||
private int checkDigit(View v) {
|
||||
int digit = -1;
|
||||
if (v == mZero) {
|
||||
digit = 0;
|
||||
} else if (v == mOne) {
|
||||
digit = 1;
|
||||
} else if (v == mTwo) {
|
||||
digit = 2;
|
||||
} else if (v == mThree) {
|
||||
digit = 3;
|
||||
} else if (v == mFour) {
|
||||
digit = 4;
|
||||
} else if (v == mFive) {
|
||||
digit = 5;
|
||||
} else if (v == mSix) {
|
||||
digit = 6;
|
||||
} else if (v == mSeven) {
|
||||
digit = 7;
|
||||
} else if (v == mEight) {
|
||||
digit = 8;
|
||||
} else if (v == mNine) {
|
||||
digit = 9;
|
||||
}
|
||||
return digit;
|
||||
}
|
||||
}
|
||||
|
||||
public void onPhoneStateChanged(String newState) {
|
||||
if (mLockPatternUtils.isEmergencyCallCapable()) {
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
|
||||
|
||||
}
|
||||
|
||||
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
|
||||
|
||||
}
|
||||
|
||||
public void onRingerModeChanged(int state) {
|
||||
|
||||
}
|
||||
|
||||
public void onTimeChanged() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -92,16 +92,17 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
|
||||
mBackSpaceButton = findViewById(R.id.backspace);
|
||||
mBackSpaceButton.setOnClickListener(this);
|
||||
|
||||
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
mOkButton = (TextView) findViewById(R.id.ok);
|
||||
|
||||
mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
|
||||
mPinText.setFocusable(false);
|
||||
|
||||
mEmergencyCallButton.setOnClickListener(this);
|
||||
mOkButton.setOnClickListener(this);
|
||||
|
||||
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
|
||||
mEmergencyCallButton.setOnClickListener(this);
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
|
||||
setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
|
||||
if (success) {
|
||||
// before closing the keyguard, report back that
|
||||
// the sim is unlocked so it knows right away
|
||||
mUpdateMonitor.reportSimPinUnlocked();
|
||||
mUpdateMonitor.reportSimUnlocked();
|
||||
mCallback.goToUnlockScreen();
|
||||
} else {
|
||||
mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
|
||||
@@ -291,9 +292,8 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
|
||||
mCallback.goToUnlockScreen();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@@ -403,7 +403,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie
|
||||
}
|
||||
|
||||
public void onPhoneStateChanged(String newState) {
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
|
||||
}
|
||||
|
||||
public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
|
||||
|
||||
Reference in New Issue
Block a user