package com.android.facelock;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import com.android.facelock.PicturePasswordUtils.Preferences;
import com.android.facelock.PicturePasswordView.OnFingerUpListener;
import com.android.internal.policy.IFaceLockCallback;
import com.android.internal.policy.IFaceLockInterface.Stub;
public class FaceLockService extends Service implements Callback
{
private final int MSG_SERVICE_CONNECTED = 0;
private final int MSG_SERVICE_DISCONNECTED = 1;
private final int MSG_CANCEL = 3;
private final int MSG_ENABLE = 47;
private final Stub binder;
protected LayoutParams mLayoutParams;
protected IFaceLockCallback mCallback;
private View mView;
private WindowManager mWindowManager;
public FaceLockService()
{
binder = new Stub()
{
@Override
public void startUi( IBinder windowToken, int x, int y, int w, int h, boolean useLiveliness ) throws RemoteException
{
DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getRealMetrics( metrics );
Log.d( "PicturePassword", "Appearing at " + ( y + h ) );
if ( y + h > metrics.heightPixels ) return;
LayoutParams p = new LayoutParams( LayoutParams.TYPE_APPLICATION_PANEL );
p.flags = LayoutParams.FLAG_HARDWARE_ACCELERATED | LayoutParams.FLAG_NOT_FOCUSABLE;
p.token = windowToken;
p.x = x;
p.y = y;
p.width = w;
p.height = h;
p.gravity = 8388659; // TODO: decompose, i have no idea what this means
FaceLockService.this.mLayoutParams = p;
FaceLockService.this.mHandler.obtainMessage( MSG_SERVICE_CONNECTED, p ).sendToTarget();
if ( PicturePasswordUtils.getLockedOut( FaceLockService.this ) )
{
lockOut( false );
}
}
@Override
public void stopUi() throws RemoteException
{
FaceLockService.this.mHandler.sendEmptyMessage( MSG_SERVICE_DISCONNECTED );
}
@Override
public void registerCallback( IFaceLockCallback callback ) throws RemoteException
{
FaceLockService.this.mCallback = callback;
}
@Override
public void unregisterCallback( IFaceLockCallback callback ) throws RemoteException
{
FaceLockService.this.mCallback = null;
}
@Override
public Bitmap getImage() throws RemoteException
{
return FaceLockService.this.mBitmap;
}
@Override
public boolean onTransact( int code, Parcel data, Parcel reply, int flags ) throws RemoteException
{
if ( code == 0x4747 )
{
reply.writeValue( getImage() );
return true;
}
return super.onTransact( code, data, reply, flags );
}
};
}
Handler mHandler;
private PicturePasswordView mPicturePassword;
private long mLastFailure;
private int mFailureCounter = 0;
private int mTotalFailureCounter = 0;
private int mSuccessStreak = 0;
private ValueAnimator mFadeOut;
private Bitmap mBitmap;
@Override
public void onCreate()
{
mHandler = new Handler( this );
Preferences prefs = new Preferences();
PicturePasswordUtils.getUnlockData( prefs, this );
mView = View.inflate( this, R.layout.service, null );
mWindowManager = ( WindowManager ) getSystemService( "window" );
mBitmap = prefs.image;
final PicturePasswordView picturePassword = ( PicturePasswordView ) mView.findViewById( R.id.picture_password );
picturePassword.setImageBitmap( prefs.image );
picturePassword.setUnlockNumber( prefs.unlockNumber, prefs.unlockNumberX, prefs.unlockNumberY );
picturePassword.setRandomize( prefs.randomize );
picturePassword.setGridSize( prefs.gridSize );
picturePassword.enforceNumber( prefs.unlockNumber );
picturePassword.setUnlockCircles( 2 );
picturePassword.setOnFingerUpListener( new OnFingerUpListener()
{
@Override
public void onFingerUp( PicturePasswordView picturePassword, boolean shouldUnlock )
{
if ( FaceLockService.this.mCallback != null )
{
try
{
FaceLockService.this.mCallback.pokeWakelock( 3000 );
}
catch ( RemoteException e )
{
e.printStackTrace();
}
}
if ( shouldUnlock )
{
mSuccessStreak++;
mPicturePassword.reset();
}
if ( mSuccessStreak == 2 )
{
if ( FaceLockService.this.mCallback != null )
{
try
{
FaceLockService.this.mCallback.unlock();
mSuccessStreak = 0;
}
catch ( RemoteException e )
{
// i hope this never happens
e.printStackTrace();
}
}
}
else if ( !shouldUnlock )
{
mSuccessStreak = 0;
if ( System.currentTimeMillis() - mLastFailure < 800 )
{
mFailureCounter++;
if ( mFailureCounter >= 2 )
{
FaceLockService.this.lockForSeconds( 5 );
mFailureCounter = 0;
}
}
mLastFailure = System.currentTimeMillis();
mTotalFailureCounter++;
PicturePasswordUtils.setFailureCounter( FaceLockService.this, mTotalFailureCounter );
if ( mTotalFailureCounter > 9 )
{
lockOut( true );
}
picturePassword.reset();
}
picturePassword.setUnlockProgress( mSuccessStreak );
}
} );
mFadeOut = ValueAnimator.ofFloat( 1, 0 );
mFadeOut.addListener( new AnimatorListener()
{
@Override
public void onAnimationStart( Animator animation )
{
}
@Override
public void onAnimationRepeat( Animator animation )
{
}
@Override
public void onAnimationEnd( Animator animation )
{
FaceLockService.this.mHandler.sendEmptyMessage( MSG_CANCEL );
}
@Override
public void onAnimationCancel( Animator animation )
{
}
} );
mFadeOut.addUpdateListener( new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate( ValueAnimator animation )
{
if ( FaceLockService.this.mLayoutParams != null )
{
FaceLockService.this.mLayoutParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE;
FaceLockService.this.mLayoutParams.alpha = ( Float ) animation.getAnimatedValue();
FaceLockService.this.mWindowManager.updateViewLayout( FaceLockService.this.mView, FaceLockService.this.mLayoutParams );
}
}
} );
mPicturePassword = picturePassword;
if ( PicturePasswordUtils.getWaitTime( this ) > System.currentTimeMillis() / 1000 )
{
mPicturePassword.setShowNumbers( false );
lockForSeconds( PicturePasswordUtils.getWaitTime( this ) - System.currentTimeMillis() / 1000 );
}
mTotalFailureCounter = PicturePasswordUtils.getFailureCounter( this );
}
public void lockOut( boolean animate )
{
Log.d( "PicturePassword", "Locking out." );
PicturePasswordUtils.setLockedOut( this, true );
if ( animate )
{
mFadeOut.start();
}
else
{
mHandler.sendEmptyMessage( MSG_CANCEL );
}
}
public void lockForSeconds( long seconds )
{
mPicturePassword.setEnabled( false );
final long sleepTime = seconds * 1000;
final PicturePasswordView view = mPicturePassword;
final Handler handler = mHandler;
PicturePasswordUtils.setWaitTime( this, seconds );
if ( view.isShowNumbers() )
{
view.setShowNumbers( false, false );
}
Thread thread = new Thread()
{
@Override
public void run()
{
try
{
sleep( sleepTime );
handler.sendEmptyMessage( MSG_ENABLE );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
}
};
thread.start();
}
@Override
public IBinder onBind( Intent intent )
{
Log.d( "PicturePasswordServ", "onBind" );
return binder;
}
// This gets overwritten by Xposed to return true if the module is loaded.
private boolean shouldFadeIn()
{
return false;
}
@Override
public boolean handleMessage( Message msg )
{
switch ( msg.what )
{
case MSG_SERVICE_CONNECTED:
mView.setVisibility( View.VISIBLE );
mWindowManager.addView( mView, ( LayoutParams ) msg.obj );
mPicturePassword.setShowNumbers( true, shouldFadeIn() );
return true;
case MSG_SERVICE_DISCONNECTED:
if ( mView != null )
{
try
{
mView.setVisibility( View.GONE );
mWindowManager.removeView( mView );
}
catch ( Exception e )
{
// oopsie!
}
}
return true;
case MSG_CANCEL:
if ( mCallback != null )
{
try
{
mCallback.cancel();
}
catch ( RemoteException e )
{
// welp
e.printStackTrace();
}
}
return true;
case MSG_ENABLE:
mPicturePassword.reset();
mPicturePassword.setShowNumbers( true, true );
mPicturePassword.setEnabled( true );
return true;
}
return false;
}
}