/* * Copyright (C) 2014 AChep@xda <ynkr.wang@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package com.bullmobi.message.ui.activities; import android.app.KeyguardManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import android.view.WindowManager; import com.bullmobi.message.App; import com.bullmobi.message.Config; import com.bullmobi.message.R; import com.bullmobi.message.Timeout; import com.bullmobi.message.services.KeyguardService; import com.bullmobi.base.Device; import com.bullmobi.base.tests.Check; import com.bullmobi.base.ui.activities.ActivityBase; import com.bullmobi.base.utils.LogUtils; import static com.bullmobi.base.Build.DEBUG; /** * Activity that contains some methods to emulate system keyguard. */ public abstract class KeyguardActivity extends ActivityBase implements Timeout.OnTimeoutEventListener { private static final String TAG = "KeyguardActivity"; public static final String EXTRA_TURN_SCREEN_ON = "turn_screen_on"; private static final int UNLOCKING_MAX_TIME = 150; // ms. private static final int PF_MAX_TIME = 2000; // ms. private BroadcastReceiver mScreenOffReceiver; private KeyguardManager mKeyguardManager; private long mUnlockingTime; private boolean mResumed; private boolean mTimeoutPaused = true; private final Timeout mTimeout = new Timeout(); private final Handler mTimeoutHandler = new Handler(); private final Handler mHandler = new Handler(); @Override public void onWindowFocusChanged(boolean windowHasFocus) { super.onWindowFocusChanged(windowHasFocus); if (DEBUG) Log.d(TAG, "On window focus changed " + windowHasFocus); if (isUnlocking()) { if (windowHasFocus) { mUnlockingTime = 0; } else { finish(); return; } } if (mResumed) { populateFlags(windowHasFocus); } } private void populateFlags(boolean manualControl) { int windowFlags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; int timeoutDelay = Config.getInstance().getTimeoutNormal(); if (manualControl) { getWindow().addFlags(windowFlags); mTimeoutPaused = false; mTimeoutHandler.removeCallbacksAndMessages(null); mTimeout.resume(); mTimeout.setTimeoutDelayed(timeoutDelay, true); } else { getWindow().clearFlags(windowFlags); mTimeoutPaused = true; mTimeout.setTimeoutDelayed(timeoutDelay, true); mTimeout.pause(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (DEBUG) Log.d(TAG, "Creating keyguard activity..."); mTimeout.registerListener(this); mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); registerScreenOffReceiver(); int flags = 0; // Handle intents Intent intent = getIntent(); if (intent != null) { // Turns screen on. if (intent.getBooleanExtra(EXTRA_TURN_SCREEN_ON, false)) flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; } // FIXME: Android dev team broke the DISMISS_KEYGUARD flag. // https://code.google.com/p/android-developer-preview/issues/detail?id=1902 if (Device.hasLollipopApi() /* bugs monster */ && !mKeyguardManager.isKeyguardSecure()) { getWindow().addFlags(flags | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); } else { getWindow().addFlags(flags | // Show activity above the system keyguard. WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } } @Override protected void onDestroy() { if (DEBUG) Log.d(TAG, "Destroying keyguard activity..."); unregisterScreenOffReceiver(); mTimeout.unregisterListener(this); mTimeout.clear(); super.onDestroy(); } /** * Registers a receiver to finish activity when screen goes off. * You will need to {@link #unregisterScreenOffReceiver() unregister} it * later. * * @see #unregisterScreenOffReceiver() */ private void registerScreenOffReceiver() { mScreenOffReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!KeyguardService.isActive) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Finalize the keyguard.").acquire(200); KeyguardActivity.this.finish(); } } }; IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1); // max allowed priority registerReceiver(mScreenOffReceiver, intentFilter); } /** * Unregisters the screen off receiver if it was registered previously. * * @see #registerScreenOffReceiver() */ private void unregisterScreenOffReceiver() { if (mScreenOffReceiver != null) { unregisterReceiver(mScreenOffReceiver); mScreenOffReceiver = null; } } @Override protected void onResume() { if (DEBUG) Log.d(TAG, "Resuming keyguard activity..."); super.onResume(); mResumed = true; mUnlockingTime = 0; populateFlags(true); overrideHomePress(true); /* // Read the system's screen off timeout setting. try { mSystemScreenOffTimeout = Settings.System.getInt( getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); } catch (Settings.SettingNotFoundException e) { mSystemScreenOffTimeout = -1; } */ } @Override protected void onPause() { if (DEBUG) Log.d(TAG, "Pausing keyguard activity..."); mResumed = false; populateFlags(false); overrideHomePress(false); mHandler.removeCallbacksAndMessages(null); super.onPause(); } /** * Notifies Xposed {@link com.bullmobi.message.plugins.xposed.OverrideHomeButton module} * to start ignoring home button press. Please, notice that it will ignore home button * click everywhere until you call {@code overrideHomePress(false)} * * @param override {@code true} to start ignoring, {@code false} to stop. * @see com.bullmobi.message.plugins.xposed.OverrideHomeButton * @see #sendBroadcast(android.content.Intent) */ private void overrideHomePress(boolean override) { Intent intent = new Intent(override ? App.ACTION_EAT_HOME_PRESS_START : App.ACTION_EAT_HOME_PRESS_STOP); sendBroadcast(intent); } @Override protected void onUserLeaveHint() { super.onUserLeaveHint(); if (DEBUG) Log.d(TAG, "User is leaving..."); /* // The user has tried to fool the keyguard. // Blame him ("You shall not pass!") // and turn the screen off. if (PowerUtils.isScreenOn(this)) { if (!mUnlocking) { Log.i(TAG, "You shall not pass!"); lock(); } else if (!mLocking) { finish(); } } */ } @Override public void onTimeoutEvent(@NonNull Timeout timeout, int event) { if (DEBUG) LogUtils.v(TAG, "TIMEOUT: " + event, 5); switch (event) { case Timeout.EVENT_CHANGED: case Timeout.EVENT_RESUMED: if (mTimeoutPaused) { mTimeoutHandler.post(new Runnable() { @Override public void run() { mTimeout.pause(); } }); } break; case Timeout.EVENT_TIMEOUT: Check.getInstance().isFalse(mTimeoutPaused); lock(); break; } } /** * Locks the device (and turns screen off). * * @return {@code true} if successful, {@code false} otherwise. * @see DevicePolicyManager#lockNow() */ public boolean lock() { DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); try { // TODO: Respect secure lock timeout settings. mUnlockingTime = 0; dpm.lockNow(); return true; } catch (SecurityException e) { return false; // Screw you owner! } } /** * Unlocks keyguard and runs {@link Runnable runnable} when unlocked. */ public void unlock(@Nullable Runnable runnable) { unlock(runnable, true); } /** * Unlocks keyguard and runs {@link Runnable runnable} when unlocked. * * @param finish {@code true} to finish activity, {@code false} to keep it * @see #unlock(Runnable) */ public void unlock(final @Nullable Runnable runnable, final boolean finish) { if (DEBUG) Log.d(TAG, "Unlocking with params: finish=" + finish); // If keyguard is disabled no need to make // a delay between calling this method and // unlocking. // Otherwise we need this delay to get new // flags applied. final long now = SystemClock.elapsedRealtime(); mUnlockingTime = now; getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); mHandler.post(new Runnable() { @Override public void run() { // Loop until flag gets applied. // TODO: Use somewhat trigger for detecting unlocking. int delta = (int) (SystemClock.elapsedRealtime() - now); if (isLocked() && !isSecure() && delta < UNLOCKING_MAX_TIME) { mHandler.postDelayed(this, 30); return; } if (runnable != null) runOnUiThread(runnable); if (finish) { finish(); Config config = Config.getInstance(); boolean animate = config.isUnlockAnimationEnabled() && !isPowerSaveMode(); overridePendingTransition(0, animate ? R.anim.activity_unlock : 0); } } }); } /** * Return whether the keyguard requires a password to unlock. * * @return {@code true} is keyguard is secure, {@code false} otherwise. */ public boolean isSecure() { return mKeyguardManager.isKeyguardSecure(); } /** * Return whether the keyguard presents. * * @return {@code true} if device is locked, {@code false} otherwise. */ public boolean isLocked() { return mKeyguardManager.isKeyguardLocked(); } /** * Return whether the keyguard is unlocking. * * @return {@code true} if the keyguard is unlocking atm, {@code false} otherwise. * @see #unlock(Runnable) * @see #PF_MAX_TIME */ public boolean isUnlocking() { return SystemClock.elapsedRealtime() - mUnlockingTime <= PF_MAX_TIME; } @NonNull public Timeout getTimeout() { return mTimeout; } @Override public void onBackPressed() { /* override back button */ } }