Animations/success state for external confirm device credentials (2/2)

Bug: 20929186
Change-Id: I4489dd37f1148fb03315ec337a546eee04660cb5
This commit is contained in:
Jorim Jaggi
2015-06-09 15:31:28 -07:00
parent 1fdd4f521c
commit ff41a9a5a6
13 changed files with 410 additions and 32 deletions

View File

@@ -1407,7 +1407,7 @@
android:theme="@style/Theme.ConfirmDeviceCredentials"/> android:theme="@style/Theme.ConfirmDeviceCredentials"/>
<activity android:name="ConfirmLockPassword" <activity android:name="ConfirmLockPassword"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/Theme.ConfirmDeviceCredentials"/> android:theme="@style/Theme.ConfirmDeviceCredentials"/>
<activity android:name=".fingerprint.FingerprintSettings" android:exported="false"/> <activity android:name=".fingerprint.FingerprintSettings" android:exported="false"/>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:interpolator="@*android:interpolator/decelerate_quart"
android:fillEnabled="true"
android:fillBefore="false" android:fillAfter="true"
android:duration="200"/>
<translate android:fromYDelta="8%" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@*android:interpolator/decelerate_quint"
android:duration="350"/>
</set>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/linear_out_slow_in"
android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="350" />

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/linear_out_slow_in"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="160" />

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0" android:toAlpha="1.0"
android:duration="160" />

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32.0dp"
android:height="32.0dp"
android:viewportWidth="40.0"
android:viewportHeight="40.0">
<path
android:pathData="M20.0,20.0m-20.0,0.0a20.0,20.0 0.0,1.0 1.0,40.0 0.0a20.0,20.0 0.0,1.0 1.0,-40.0 0.0"
android:fillColor="?android:attr/colorAccent"/>
<path
android:pathData="M11.2,21.41l1.63,-1.619999 4.17,4.169998 10.59,-10.589999 1.619999,1.63 -12.209999,12.209999z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -225,6 +225,11 @@
<item name="android:layout_height">wrap_content</item> <item name="android:layout_height">wrap_content</item>
</style> </style>
<style name="ConfirmDeviceCredentialsAnimationStyle" parent="@*android:style/Animation.Material.Activity">
<item name="android:activityOpenEnterAnimation">@anim/confirm_credential_open_enter</item>
<item name="android:activityOpenExitAnimation">@anim/confirm_credential_open_exit</item>
</style>
<style name="Transparent"> <style name="Transparent">
<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>

View File

@@ -199,7 +199,7 @@
<item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item> <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_950</item>
<item name="android:windowActionBar">false</item> <item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
<item name="preferenceBackgroundColor">@color/confirm_device_credential_dark_background</item> <item name="android:windowBackground">@color/confirm_device_credential_dark_background</item>
<item name="confirmDeviceCredentialsSideMargin">32dp</item> <item name="confirmDeviceCredentialsSideMargin">32dp</item>
<item name="confirmDeviceCredentialsTopMargin">32dp</item> <item name="confirmDeviceCredentialsTopMargin">32dp</item>
@@ -208,6 +208,8 @@
<item name="@*android:regularColor">@color/lock_pattern_view_regular_color_dark</item> <item name="@*android:regularColor">@color/lock_pattern_view_regular_color_dark</item>
<item name="@*android:successColor">@color/lock_pattern_view_regular_color_dark</item> <item name="@*android:successColor">@color/lock_pattern_view_regular_color_dark</item>
<item name="@*android:errorColor">@color/lock_pattern_view_error_color</item> <item name="@*android:errorColor">@color/lock_pattern_view_error_color</item>
<item name="android:windowAnimationStyle">@style/ConfirmDeviceCredentialsAnimationStyle</item>
</style> </style>
<style name="Theme.FingerprintEnroll" parent="@*android:style/Theme.Material.Settings.NoActionBar"> <style name="Theme.FingerprintEnroll" parent="@*android:style/Theme.Material.Settings.NoActionBar">

View File

@@ -16,17 +16,23 @@
package com.android.settings; package com.android.settings;
import android.app.Fragment;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.WindowManager; import android.view.WindowManager;
public class ConfirmDeviceCredentialBaseActivity extends SettingsActivity { public abstract class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
private boolean mRestoring;
private boolean mDark;
private boolean mEnterAnimationPending;
@Override @Override
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
if (getIntent().getBooleanExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) { if (getIntent().getBooleanExtra(ConfirmDeviceCredentialBaseFragment.DARK_THEME, false)) {
setTheme(R.style.Theme_ConfirmDeviceCredentialsDark); setTheme(R.style.Theme_ConfirmDeviceCredentialsDark);
mDark = true;
} }
super.onCreate(savedState); super.onCreate(savedState);
boolean deviceLocked = getSystemService(KeyguardManager.class).isKeyguardLocked(); boolean deviceLocked = getSystemService(KeyguardManager.class).isKeyguardLocked();
@@ -41,6 +47,7 @@ public class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true); getActionBar().setHomeButtonEnabled(true);
} }
mRestoring = savedState != null;
} }
@Override @Override
@@ -51,4 +58,37 @@ public class ConfirmDeviceCredentialBaseActivity extends SettingsActivity {
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
public void onResume() {
super.onResume();
if (!isChangingConfigurations() && !mRestoring && mDark) {
prepareEnterAnimation();
mEnterAnimationPending = true;
}
}
private ConfirmDeviceCredentialBaseFragment getFragment() {
Fragment fragment = getFragmentManager().findFragmentById(R.id.main_content);
if (fragment != null && fragment instanceof ConfirmDeviceCredentialBaseFragment) {
return (ConfirmDeviceCredentialBaseFragment) fragment;
}
return null;
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
if (mEnterAnimationPending) {
startEnterAnimation();
}
}
public void prepareEnterAnimation() {
getFragment().prepareEnterAnimation();
}
public void startEnterAnimation() {
getFragment().startEnterAnimation();
}
} }

View File

@@ -45,6 +45,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
private FingerprintUiHelper mFingerprintHelper; private FingerprintUiHelper mFingerprintHelper;
private boolean mAllowFpAuthentication; private boolean mAllowFpAuthentication;
protected Button mCancelButton;
protected ImageView mFingerprintIcon;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -56,14 +58,15 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
mCancelButton = (Button) view.findViewById(R.id.cancelButton);
mFingerprintIcon = (ImageView) view.findViewById(R.id.fingerprintIcon);
mFingerprintHelper = new FingerprintUiHelper( mFingerprintHelper = new FingerprintUiHelper(
(ImageView) view.findViewById(R.id.fingerprintIcon), mFingerprintIcon,
(TextView) view.findViewById(R.id.errorText), this); (TextView) view.findViewById(R.id.errorText), this);
boolean showCancelButton = getActivity().getIntent().getBooleanExtra( boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
SHOW_CANCEL_BUTTON, false); SHOW_CANCEL_BUTTON, false);
Button cancelButton = (Button) view.findViewById(R.id.cancelButton); mCancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE);
cancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE); mCancelButton.setOnClickListener(new View.OnClickListener() {
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
getActivity().finish(); getActivity().finish();
@@ -100,4 +103,10 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
@Override @Override
public void onFingerprintIconVisibilityChanged(boolean visible) { public void onFingerprintIconVisibilityChanged(boolean visible) {
} }
public void prepareEnterAnimation() {
}
public void startEnterAnimation() {
}
} }

View File

@@ -19,9 +19,12 @@ package com.android.settings;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TextViewInputDisabler; import com.android.internal.widget.TextViewInputDisabler;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import android.app.Fragment; import android.app.Fragment;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
@@ -39,11 +42,14 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import java.util.ArrayList;
public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity { public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
public static class InternalActivity extends ConfirmLockPassword { public static class InternalActivity extends ConfirmLockPassword {
@@ -89,6 +95,9 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
private boolean mIsAlpha; private boolean mIsAlpha;
private InputMethodManager mImm; private InputMethodManager mImm;
private boolean mUsingFingerprint = false; private boolean mUsingFingerprint = false;
private AppearAnimationUtils mAppearAnimationUtils;
private DisappearAnimationUtils mDisappearAnimationUtils;
private boolean mBlockImm;
// required constructor for fragments // required constructor for fragments
public ConfirmLockPasswordFragment() { public ConfirmLockPasswordFragment() {
@@ -144,6 +153,14 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
int currentType = mPasswordEntry.getInputType(); int currentType = mPasswordEntry.getInputType();
mPasswordEntry.setInputType(mIsAlpha ? currentType mPasswordEntry.setInputType(mIsAlpha ? currentType
: (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD)); : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
220, 2f /* translationScale */, 1f /* delayScale*/,
AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.linear_out_slow_in));
mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
110, 1f /* translationScale */,
0.5f /* delayScale */, AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.fast_out_linear_in));
return view; return view;
} }
@@ -162,6 +179,43 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
: R.string.lockpassword_invalid_pin; : R.string.lockpassword_invalid_pin;
} }
@Override
public void prepareEnterAnimation() {
super.prepareEnterAnimation();
mHeaderTextView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
mPasswordEntry.setAlpha(0f);
mFingerprintIcon.setAlpha(0f);
mBlockImm = true;
}
private View[] getActiveViews() {
ArrayList<View> result = new ArrayList<>();
result.add(mHeaderTextView);
result.add(mDetailsTextView);
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(mCancelButton);
}
result.add(mPasswordEntry);
if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
result.add(mFingerprintIcon);
}
return result.toArray(new View[] {});
}
@Override
public void startEnterAnimation() {
super.startEnterAnimation();
mAppearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
@Override
public void run() {
mBlockImm = false;
resetState();
}
});
}
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
@@ -199,9 +253,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
@Override @Override
protected void authenticationSucceeded() { protected void authenticationSucceeded() {
Intent intent = new Intent(); startDisappearAnimation(new Intent());
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
} }
@Override @Override
@@ -210,6 +262,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
} }
private void resetState() { private void resetState() {
if (mBlockImm) return;
mPasswordEntry.setEnabled(true); mPasswordEntry.setEnabled(true);
mPasswordEntryInputDisabler.setInputEnabled(true); mPasswordEntryInputDisabler.setInputEnabled(true);
if (shouldAutoShowSoftKeyboard()) { if (shouldAutoShowSoftKeyboard()) {
@@ -222,7 +275,7 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
} }
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
if (!hasFocus) { if (!hasFocus || mBlockImm) {
return; return;
} }
// Post to let window focus logic to finish to allow soft input show/hide properly. // Post to let window focus logic to finish to allow soft input show/hide properly.
@@ -312,11 +365,28 @@ public class ConfirmLockPassword extends ConfirmDeviceCredentialBaseActivity {
}); });
} }
private void startDisappearAnimation(final Intent intent) {
if (getActivity().getThemeResId() == R.style.Theme_ConfirmDeviceCredentialsDark) {
mDisappearAnimationUtils.startAnimation(getActiveViews(), new Runnable() {
@Override
public void run() {
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
getActivity().overridePendingTransition(
R.anim.confirm_credential_close_enter,
R.anim.confirm_credential_close_exit);
}
});
} else {
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
}
}
private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs) { private void onPasswordChecked(boolean matched, Intent intent, int timeoutMs) {
mPasswordEntryInputDisabler.setInputEnabled(true); mPasswordEntryInputDisabler.setInputEnabled(true);
if (matched) { if (matched) {
getActivity().setResult(RESULT_OK, intent); startDisappearAnimation(intent);
getActivity().finish();
} else { } else {
if (timeoutMs > 0) { if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline( long deadline = mLockPatternUtils.setLockoutAttemptDeadline(

View File

@@ -22,10 +22,21 @@ import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient; import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockPatternView.Cell;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity; import android.app.Activity;
import android.app.Fragment;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.AsyncTask; import android.os.AsyncTask;
@@ -33,11 +44,16 @@ import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView; import android.widget.TextView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@@ -70,7 +86,8 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
return false; return false;
} }
public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment { public static class ConfirmLockPatternFragment extends ConfirmDeviceCredentialBaseFragment
implements AppearAnimationCreator<Object> {
// how long we wait to clear a wrong pattern // how long we wait to clear a wrong pattern
private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000; private static final int WRONG_PATTERN_CLEAR_TIMEOUT_MS = 2000;
@@ -93,6 +110,9 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
private CharSequence mHeaderText; private CharSequence mHeaderText;
private CharSequence mDetailsText; private CharSequence mDetailsText;
private AppearAnimationUtils mAppearAnimationUtils;
private DisappearAnimationUtils mDisappearAnimationUtils;
// required constructor for fragments // required constructor for fragments
public ConfirmLockPatternFragment() { public ConfirmLockPatternFragment() {
@@ -144,6 +164,20 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
getActivity().finish(); getActivity().finish();
} }
} }
mAppearAnimationUtils = new AppearAnimationUtils(getContext(),
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 2f /* translationScale */,
1.3f /* delayScale */, AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.linear_out_slow_in));
mDisappearAnimationUtils = new DisappearAnimationUtils(getContext(),
125, 4f /* translationScale */,
0.3f /* delayScale */, AnimationUtils.loadInterpolator(
getContext(), android.R.interpolator.fast_out_linear_in),
new AppearAnimationUtils.RowTranslationScaler() {
@Override
public float getRowTranslationScale(int row, int numRows) {
return (float)(numRows - row) / numRows;
}
});
return view; return view;
} }
@@ -187,6 +221,51 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
} }
} }
@Override
public void prepareEnterAnimation() {
super.prepareEnterAnimation();
mHeaderTextView.setAlpha(0f);
mCancelButton.setAlpha(0f);
mLockPatternView.setAlpha(0f);
mDetailsTextView.setAlpha(0f);
mFingerprintIcon.setAlpha(0f);
}
private Object[][] getActiveViews() {
ArrayList<ArrayList<Object>> result = new ArrayList<>();
result.add(new ArrayList<Object>(Collections.singletonList(mHeaderTextView)));
result.add(new ArrayList<Object>(Collections.singletonList(mDetailsTextView)));
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<Object>(Collections.singletonList(mCancelButton)));
}
LockPatternView.CellState[][] cellStates = mLockPatternView.getCellStates();
for (int i = 0; i < cellStates.length; i++) {
ArrayList<Object> row = new ArrayList<>();
for (int j = 0; j < cellStates[i].length; j++) {
row.add(cellStates[i][j]);
}
result.add(row);
}
if (mFingerprintIcon.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<Object>(Collections.singletonList(mFingerprintIcon)));
}
Object[][] resultArr = new Object[result.size()][cellStates[0].length];
for (int i = 0; i < result.size(); i++) {
ArrayList<Object> row = result.get(i);
for (int j = 0; j < row.size(); j++) {
resultArr[i][j] = row.get(j);
}
}
return resultArr;
}
@Override
public void startEnterAnimation() {
super.startEnterAnimation();
mLockPatternView.setAlpha(1f);
mAppearAnimationUtils.startAnimation2d(getActiveViews(), null, this);
}
private void updateStage(Stage stage) { private void updateStage(Stage stage) {
switch (stage) { switch (stage) {
case NeedToUnlock: case NeedToUnlock:
@@ -242,9 +321,27 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
@Override @Override
protected void authenticationSucceeded() { protected void authenticationSucceeded() {
Intent intent = new Intent(); startDisappearAnimation(new Intent());
getActivity().setResult(Activity.RESULT_OK, intent); }
getActivity().finish();
private void startDisappearAnimation(final Intent intent) {
if (getActivity().getThemeResId() == R.style.Theme_ConfirmDeviceCredentialsDark) {
mLockPatternView.clearPattern();
mDisappearAnimationUtils.startAnimation2d(getActiveViews(),
new Runnable() {
@Override
public void run() {
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
getActivity().overridePendingTransition(
R.anim.confirm_credential_close_enter,
R.anim.confirm_credential_close_exit);
}
}, this);
} else {
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();
}
} }
@Override @Override
@@ -357,8 +454,7 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
boolean matched, Intent intent, int timeoutMs) { boolean matched, Intent intent, int timeoutMs) {
mLockPatternView.setEnabled(true); mLockPatternView.setEnabled(true);
if (matched) { if (matched) {
getActivity().setResult(Activity.RESULT_OK, intent); startDisappearAnimation(intent);
getActivity().finish();
} else { } else {
if (timeoutMs > 0) { if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline( long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
@@ -394,5 +490,52 @@ public class ConfirmLockPattern extends ConfirmDeviceCredentialBaseActivity {
} }
}.start(); }.start();
} }
@Override
public void createAnimation(Object obj, long delay,
long duration, float translationY, final boolean appearing,
Interpolator interpolator,
final Runnable finishListener) {
if (obj instanceof LockPatternView.CellState) {
final LockPatternView.CellState animatedCell = (LockPatternView.CellState) obj;
if (appearing) {
animatedCell.scale = 0.0f;
animatedCell.alpha = 1.0f;
}
animatedCell.translateY = appearing ? translationY : 0;
ValueAnimator animator = ValueAnimator.ofFloat(animatedCell.translateY,
appearing ? 0 : translationY);
animator.setInterpolator(interpolator);
animator.setDuration(duration);
animator.setStartDelay(delay);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedFraction = animation.getAnimatedFraction();
if (appearing) {
animatedCell.scale = animatedFraction;
} else {
animatedCell.alpha = 1 - animatedFraction;
}
animatedCell.translateY = (float) animation.getAnimatedValue();
mLockPatternView.invalidate();
}
});
if (finishListener != null) {
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finishListener.run();
}
});
}
animator.start();
mLockPatternView.invalidate();
} else {
mAppearAnimationUtils.createAnimation((View) obj, delay, duration, translationY,
appearing, interpolator, finishListener);
}
}
} }
} }

View File

@@ -18,7 +18,6 @@ package com.android.settings.fingerprint;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.Vibrator;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -31,8 +30,6 @@ import com.android.settings.R;
public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback { public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback {
private static final long ERROR_TIMEOUT = 1300; private static final long ERROR_TIMEOUT = 1300;
private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
private ImageView mIcon; private ImageView mIcon;
private TextView mErrorTextView; private TextView mErrorTextView;
@@ -92,7 +89,7 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
@Override @Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
vibrateFingerprintSuccess(); mIcon.setImageResource(R.drawable.ic_fingerprint_success);
mCallback.onAuthenticated(); mCallback.onAuthenticated();
} }
@@ -101,21 +98,12 @@ public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallba
return; return;
} }
vibrateFingerprintError();
mIcon.setImageResource(R.drawable.ic_fingerprint_error); mIcon.setImageResource(R.drawable.ic_fingerprint_error);
mErrorTextView.setText(error); mErrorTextView.setText(error);
mErrorTextView.removeCallbacks(mResetErrorTextRunnable); mErrorTextView.removeCallbacks(mResetErrorTextRunnable);
mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT); mErrorTextView.postDelayed(mResetErrorTextRunnable, ERROR_TIMEOUT);
} }
private void vibrateFingerprintError() {
mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
}
private void vibrateFingerprintSuccess() {
mIcon.getContext().getSystemService(Vibrator.class).vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
}
private Runnable mResetErrorTextRunnable = new Runnable() { private Runnable mResetErrorTextRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {