背景
先说说背景吧,这是本人从WinCE系统转到Android之后,接到的第一个任务就是修改Android原生的解锁界面,之前看了两个星期的书和网络博客,Java的也有、Android应用开发的也有、Linux开发的也有、Android框架介绍的也有。然后写了几个APK试了了一下,觉得自己有能力了,便充满自信地找到组长接任务。组长没有说什么,拿出一个竞争对手公司的样机,玩了几下拿个我,说道:这是竞争对手公司的方案,他们的解锁效果不错,很方便,你看看能不能也做出来。
说实话接到这个任务当时真的有点失望,心里面一直想,当初做WinCE做的几乎全是驱动,整天和寄存器、指针、协议打交道,现在转到Android,做这些应用的东西真的不太习惯。不过转头一想,不管什么东西,要做就要做好,慢慢来嘛,于是拿走样机回到工位上,安心开始研究。
样机解锁界面效果类似如下
其实这也是我后面做出的效果,样机因为没有ROOT不好调试截图,功能是将解锁的图标添加了最近运行的运用的图标,这样的话更方便用户去使用
1.1.
Android锁屏功能分析Android锁屏相关的代码在以下几个路径:
锁屏的具体实现:
\frameworks\base\policy\src\com\android\internal\policy\impl |
其中的主要代码如下:
锁屏控件的View类
\frameworks\base\core\java\com\android\internal\widget\multiwaveview |
锁屏控件使用到的资源
\frameworks\base\core\res\res\values-sw600dp-land\arrays.xml \frameworks\base\core\res\res\drawableXXX |
Android上常用的锁屏方法有以下几种:默认锁屏方式(LockScreen)、SIM卡解锁方式(SimUnlockScreen)、图案解锁方式(PatternUnlockScreen)、密码解锁方式(PasswordUnlockScreen)、账号解锁方式(AccountUnlockScreen),这些解锁方式都有对应的源码实现,我们这里讨论的是最常用的默认解锁方式,在Android4.0之后,解锁控件变为“波纹解锁”,即如概述介绍的那样,通过控制中心的圆圈来实现解锁,这种解锁方式,实际上可以进一步增强。
1.开机启动后执行到PhoneWindowManager.systemReady()。
2.调用KeyguardViewMediator.onSystemReady()进行待机锁屏及解锁逻辑。
3.KeyguardViewMediator是整个待机解锁屏业务的调度器,负责调度锁屏界面的相关动作及查询解锁屏状态。
1.1.2. KeyguardViewMediator的作用
1.查询锁屏状态,及当前处于锁屏状态还是已解锁状态,PhoneWindowManager持有KeyguardViewMediator的引用,当用户触摸屏幕或者按下某个键是,PhoneWindowManager会通过KeyguardViewMediator查询锁屏状态(锁定/解锁),进行不同的响应处理。如果处于锁定状态,系统输入事件会受到限制。
2.响应电源事件(黑/亮屏)。判断锁屏界面应该处于什么状态(显示或者重置)。手机黑屏后,锁屏界面马上就会显示出来,以便下一次亮屏后,马上就能显示锁屏界面,而不会出现闪烁或延时。
3.其他应用程序或者服务也可以请求禁止锁屏(通过调用KeyguardViewMediator的setKeyguardEnabled(boolean)方法)。例如接听来电界面。
KeyguardViewMediator类在WindowManagerPolicy(在手机系统中是PhoneWindowManager实例)初始化时被创建,并运行在它的线程上,锁屏的UI界面也是在这个线程上创建及显示的。KeyguardViewMediator类提供的状态查询api可以被诸如android.view.WindowManager、com.android.server.InputManager等其它线程调用,所以,KeyguardViewMediator类上的这些api方法都是线程同步的(synchronized)。
1.1.3. KeyguardViewMediator可以进行的调度操作
1) 点亮屏幕pokeWakelock();
2) 报告锁屏权限验证是否成功keyguardDone(boolean);
3) 响应SIM卡状态变化并对锁屏界面做相应的调整onSimStateChanged()。
4) 调度待机锁屏UI界面的管理,包括:
1.显示handleShow ()、
2.隐藏handleHide ()、
3.重置handleReset ()、
4.点亮屏幕handleWakeWhenReady()等。
KeyguardViewMediator实现这部分调度是通过持有一个KeyguardViewManager来实现的。总之KeyguardUpdateMonitor是所有会影响整个待机解/锁屏业务的事件的监控器。(除了作为监控器,它还发挥着类似上下文的作用,也许我们应该把这个类命名为(KeyguardContext)。它监控诸如时间改变、电池状态改变、时区改变、SIM卡状态变化、电话状态变化、电话信号变化等事件。它是一个观察者模式的被观察对象。观察者通过调用KeyguardUpdateMonitor的以下方法进行注册,观察自己感兴趣的变化。
registerInfoCallback(InfoCallback)registerSimStateCallback(SimStateCallback) |
KeyguardUpdateMonitor的观察者包括KeyguardViewMediator、LockScreen、PatternUnlockScreen、AccountUnlockScreen、PasswordUnlockScreen、SimUnlockScreen等。观察者通过调用KeyguardUpdateMonitor的removeCallback(Object)取消观察。
KeyguardViewManager负责管理待机屏UI界面的创建、显示、隐藏、重置以及通过一个回调KeyguardViewCallback通知调度器KeyguardViewMediator进行相关的调度。
LockPatternKeyguardView(KeyguardViewBase)是所有锁屏和解锁UI界面的宿主。它有2个模式Mode.
LockScreen和Mode. UnlockScreen。它负责根据当前上下文环境切换当前应该显示的待机屏。
它提供一个回调给当前显示的待机屏并处理其回调,如果回调动作是自己处理不了的,则继续报告给KeyguardViewMediator进行处理。
锁屏界面就是LockScreen;解锁界面包括SIM卡解锁SimUnlockScreen、图案解锁PatternUnlockScreen、密码解锁PasswordUnlockScreen、帐号解锁AccountUnlockScreen
解锁成功后,锁屏流程转到KeyguardViewMediator的keyguardDone(boolean, boolean) 进行后续的流程(如转到Launcher桌面)。
解锁界面布局在LockScreen类的构造函数中进行,LockScreen构造函数内容如下:
点击(此处)折叠或打开
- LockScreen(Context context, Configuration configuration, LockPatternUtils
lockPatternUtils, - KeyguardUpdateMonitor updateMonitor,
- KeyguardScreenCallback callback) {
- super(context);
- mLockPatternUtils = lockPatternUtils;
- mUpdateMonitor = updateMonitor;
- mCallback = callback;
- mEnableMenuKeyInLockScreen = shouldEnableMenuKey();
- mCreationOrientation = configuration.orientation;
- mKeyboardHidden = configuration.hardKeyboardHidden;
- if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
- Log.v(TAG, "***** CREATING LOCK SCREEN", new RuntimeException());
- Log.v(TAG, "Cur orient=" + mCreationOrientation
- + " res orient=" + context.getResources().getConfiguration().orientation);
- }
- final LayoutInflater inflater = LayoutInflater.from(context);
- if (DBG) Log.v(TAG, "Creation
orientation = " + mCreationOrientation); - if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
- inflater.inflate(R.layout.keyguard_screen_tab_unlock, this, true);
- } else {
- inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true);
- }
- mStatusViewManager = new KeyguardStatusViewManager(this, mUpdateMonitor, mLockPatternUtils,
- mCallback, false);
- setFocusable(true);
- setFocusableInTouchMode(true);
- setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
- mSilentMode = isSilentMode();
- mUnlockWidget = findViewById(R.id.unlock_widget);
- if (mUnlockWidget instanceof SlidingTab) {
- SlidingTab slidingTabView = (SlidingTab) mUnlockWidget;
- slidingTabView.setHoldAfterTrigger(true, false);
- slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label);
- slidingTabView.setLeftTabResources(
- R.drawable.ic_jog_dial_unlock,
- R.drawable.jog_tab_target_green,
- R.drawable.jog_tab_bar_left_unlock,
- R.drawable.jog_tab_left_unlock);
- SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView);
- slidingTabView.setOnTriggerListener(slidingTabMethods);
- mUnlockWidgetMethods = slidingTabMethods;
- } else if (mUnlockWidget instanceof WaveView) {
- WaveView waveView = (WaveView) mUnlockWidget;
- WaveViewMethods waveViewMethods = new WaveViewMethods(waveView);
- waveView.setOnTriggerListener(waveViewMethods);
- mUnlockWidgetMethods = waveViewMethods;
- } else if (mUnlockWidget instanceof MultiWaveView) {
- MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget;
- MultiWaveViewMethods multiWaveViewMethods = new MultiWaveViewMethods(multiWaveView);
- multiWaveView.setOnTriggerListener(multiWaveViewMethods);
- mUnlockWidgetMethods = multiWaveViewMethods;
- } else {
- throw new IllegalStateException("Unrecognized
unlock widget: " + mUnlockWidget); - }
- // Update widget with initial ring state
- mUnlockWidgetMethods.updateResources(context);
- if (DBG) Log.v(TAG, "***
LockScreen accel is " - + (mUnlockWidget.isHardwareAccelerated() ? "on":"off"));
- }
通过添加打印发现在480分辨率下采用的Layout文件为\layout-sw480dp\keyguard_screen_tab_unlock_land.xml,此文件的内容如下:
点击(此处)折叠或打开
- <?xml version="1.0" encoding="utf-8"?>
- <!--
- **
- ** Copyright 2009, 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.
- */
- -->
- <!-- This is the general lock screen which shows information about the
- state of the device, as well as instructions on how to get past it
- depending on the state of the device.-->
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tabunlock="http://schemas.android.com/apk/res/com.android.tabunlock"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:id="@+id/root">
- <!-- left side: status and music -->
- <RelativeLayout
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_width="0dip"
- android:gravity="center">
- <RelativeLayout android:id="@+id/transport_bg_protect"
- android:layout_width="512dip"
- android:layout_height="wrap_content">
- <!-- Music transport control underneath -->
- <include android:id="@+id/transport"
- layout="@layout/keyguard_transport_control"
- android:layout_row="0"
- android:layout_column="0"
- android:layout_rowSpan="3"
- android:layout_columnSpan="1"
- android:layout_gravity="fill"
- android:layout_width="match_parent"
- android:layout_height="512dip"
- />
- <include layout="@layout/keyguard_screen_status_land"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="50dip"
- android:layout_marginTop="50dip"
- android:layout_marginBottom="82dip"
- android:layout_marginRight="64dip"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"/>
- </RelativeLayout>
- </RelativeLayout>
- <!-- right side -->
- <RelativeLayout
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_width="0dip"
- android:gravity="center_horizontal|center_vertical">
- <TextView
- android:id="@+id/screenLocked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:gravity="center"
- android:layout_marginTop="12dip"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:drawablePadding="4dip"/>
- <com.android.internal.widget.multiwaveview.MultiWaveView
- android:id="@+id/unlock_widget"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_rowSpan="7"
- android:layout_gravity="center_vertical|center_horizontal"
- android:targetDrawables="@array/lockscreen_targets_with_camera"
- android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera"
- android:directionDescriptions="@array/lockscreen_direction_descriptions"
- android:handleDrawable="@drawable/ic_lockscreen_handle"
- android:waveDrawable="@drawable/ic_lockscreen_outerring"
- android:outerRadius="@dimen/multiwaveview_target_placement_radius"
- android:snapMargin="@dimen/multiwaveview_snap_margin"
- android:hitRadius="@dimen/multiwaveview_hit_radius"
- android:rightChevronDrawable="@drawable/ic_lockscreen_chevron_right"
- android:feedbackCount="3"
- android:vibrationDuration="20"
- android:horizontalOffset="0dip"
- android:verticalOffset="0dip"
- />
- <!-- emergency call button shown when sim is PUKd and tab_selector is hidden -->
- <Button
- android:id="@+id/emergencyCallButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="80dip"
- android:layout_marginBottom="80dip"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:drawableLeft="@drawable/ic_emergency"
- style="@style/Widget.Button.Transparent"
- android:drawablePadding="8dip"
- android:visibility="gone"/>
- </RelativeLayout>>
- </LinearLayout>
从文件中可以看出,解锁界面的数字时钟、充电状态、波纹解锁等控件均在其中布局,对解锁控件的大小修改也是通过修改该文件进行的。
对于Android4.0默认的LockScreen,采用的是MultiWaveView控件,LockScreen中创建该控件的代码如下:
点击(此处)折叠或打开
- mUnlockWidget = findViewById(R.id.unlock_widget);
- if (mUnlockWidget instanceof SlidingTab) {
- SlidingTab slidingTabView = (SlidingTab) mUnlockWidget;
- slidingTabView.setHoldAfterTrigger(true, false);
- slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label);
- slidingTabView.setLeftTabResources(
- R.drawable.ic_jog_dial_unlock,
- R.drawable.jog_tab_target_green,
- R.drawable.jog_tab_bar_left_unlock,
- R.drawable.jog_tab_left_unlock);
- SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView);
- slidingTabView.setOnTriggerListener(slidingTabMethods);
- mUnlockWidgetMethods = slidingTabMethods;
- } else if (mUnlockWidget instanceof WaveView) {
- WaveView waveView = (WaveView) mUnlockWidget;
- WaveViewMethods waveViewMethods = new WaveViewMethods(waveView);
- waveView.setOnTriggerListener(waveViewMethods);
- mUnlockWidgetMethods = waveViewMethods;
- } else if (mUnlockWidget instanceof MultiWaveView) {
- MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget;
- MultiWaveViewMethods multiWaveViewMethods = new MultiWaveViewMethods(multiWaveView);
- multiWaveView.setOnTriggerListener(multiWaveViewMethods);
- mUnlockWidgetMethods = multiWaveViewMethods;
- } else {
- throw new IllegalStateException("Unrecognized
unlock widget: " + mUnlockWidget); - }
这是LockScreen的构造函数中的代码,函数根据R.id.unlock_widget定义的类型选择不同的控件类,其中第一种是Android2.3的滑动解锁类,第二种的简单的波纹解锁类,第三种才是我们使用的MultiWaveView类,函数并创建了一个MultiWaveViewMethods类,这个类实际上是为了更好地使用而进行的封装,它的代码如下:
点击(此处)折叠或打开
- class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener,
- UnlockWidgetCommonMethods {
- private final MultiWaveView mMultiWaveView;
- private boolean mCameraDisabled;
- MultiWaveViewMethods(MultiWaveView multiWaveView) {
- mMultiWaveView = multiWaveView;
- final boolean cameraDisabled = mLockPatternUtils.getDevicePolicyManager()
- .getCameraDisabled(null);
- if (cameraDisabled) {
- Log.v(TAG, "Camera disabled by Device Policy");
- mCameraDisabled = true;
- } else {
- // Camera is enabled if resource is initially defined for MultiWaveView
- // in the lockscreen layout file
- mCameraDisabled = mMultiWaveView.getTargetResourceId()
- != R.array.lockscreen_targets_with_camera;
- }
- }