本篇小文对来电接听InCallScreen界面进行源码分析,该文基于android5.1进行源码分析,由于使用了厂商提供的源码版本,和google官方发布的版本会有所差异。
1. 效果图
我们先来看样机的实际运行效果图
我们通过图片定位到:
packages/apps/InCallUI/res/values/array.xml
下面我们来看下array.xml中的代码
<array name="incoming_call_widget_audio_with_sms_targets">
<item>@drawable/ic_lockscreen_answer</item>
<!--item>@drawable/ic_lockscreen_text</item-->
<item>@null</item>
<item>@drawable/ic_lockscreen_decline</item>
<item>@null</item>"
</array>
我们看到,在这个定义了接听/拒接/短信这三个功能。
通过“incoming_call_widget_audio_with_sms_targets”来调用这个array,继续看这个调用的地方
packages/apps/InCallUI/res/layout/answer_fragment.xml
我们来看下这个文件的具体调用:
<com.android.incallui.GlowPadWrapper
...............
dc:targetDrawables="@array/incoming_call_widget_audio_with_sms_targets"
dc:handleDrawable="@drawable/ic_incall_audio_handle"
dc:outerRingDrawable="@drawable/ic_lockscreen_outerring"
......................
我们看到,incoming_call_widget_audio_with_sms_targets在这里进行了调用,并调用了GlowPadWrapper该函数来进行相关处理,由于对GlowPadWrapper函数不熟悉,下面我们先来介绍一下这个函数的基本情况。
最中间圆圈的按钮: dc:handleDrawable=”@drawable/ic_incall_audio_handle”
最外面圆圈的颜色设置: dc:outerRingDrawable=”@drawable/ic_lockscreen_outerring”
2. GlowPadWrapper.java
我们看到在GlowPadWrapper.java是继承GlowPadView而来的,在这个函数里对相关的空间进行了处理,我们来看一下:
@Override
public void onTrigger(View v, int target) {
Log.d(this, "onTrigger()");
final int resId = getResourceIdForTarget(target);
switch (resId) {
case R.drawable.ic_lockscreen_answer:
mAnswerListener.onAnswer(VideoProfile.VideoState.AUDIO_ONLY, getContext());
mTargetTriggered = true;
break;
case R.drawable.ic_lockscreen_decline:
mAnswerListener.onDecline();
mTargetTriggered = true;
break;
case R.drawable.ic_lockscreen_text:
mAnswerListener.onText();
mTargetTriggered = true;
//SPRD bug 424648 {@
TelecomManager telecomManager = SprdUtils.getTelecommService(getContext());
telecomManager.silenceRinger();
//@}
break;
case R.drawable.ic_lockscreen_answer_video://SPRD: add for can't accept video call
case R.drawable.ic_videocam:
mAnswerListener.onAnswer(VideoProfile.VideoState.BIDIRECTIONAL, getContext());
mTargetTriggered = true;
break;
case R.drawable.ic_toolbar_video_off:
InCallPresenter.getInstance().declineUpgradeRequest(getContext());
mTargetTriggered = true;
break;
default:
// Code should never reach here.
Log.e(this, "Trigger detected on unhandled resource. Skipping.");
}
}
在这里对
ic_lockscreen_answer 、
ic_lockscreen_decline
ic_lockscreen_text
对接听,拒接,短信进行了处理,这里我们先放一下,我们看下GlowPadView.java这个函数的情况。
public class GlowPadView extends View {}
我们看到,其实也是extends了view,所以在这里我们看到很熟悉的一些函数
protected void onDraw(Canvas canvas){};
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {};
相信大家对view的常用的函数都已经比较熟悉了,我们就不在这里更进一步的分析,
到这里我们知道:
GlowPadView.java进行布局;
GlowPadWrapper.java对按钮进行监测;
3.0 LayoutInflater.inflate
在往下看之前我们先来看一下inflate这个是起什么作用的,在
sw/frameworks/base/core/java/android/view/LayoutInflater.java
—->
public View inflate(int resource, ViewGroup root) {}
public View inflate(XmlPullParser parser, ViewGroup root){}
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {}
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {}
在这几个定义的注释开头,我们都能看到这样一句话,
Inflate a new view hierarchy from the specified XML node.
实例化一个xml到制定的layout里和setContent有一些区别:
setContentView()一旦调用, layout就会立刻显示UI;而inflate只会把Layout形成一个以view类实现成的对象,有需要时再用setContentView(view)显示出来。一般在activity中通过setContentView()将界面显示出来,但是如果在非activity中如何对控件布局设置操作了,这就需要LayoutInflater动态加载。
public View inflate(int Resourece,ViewGroup root)
作用:填充一个新的视图层次结构从指定的XML资源文件中
reSource:View的layout的ID
root: 生成的层次结构的根视图
return 填充的层次结构的根视图。如果参数root提供了,那么root就是根视图;否则填充的XML文件的根就是根视图。
public View inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义
resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。
root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个 参
数attachToRoot为true,就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附
加到resource对象的根布局对象上,也就是布局文件resource的最外层的View上,比如是一个
LinearLayout或者其它的Layout对象。
attachToRoot:是否将root附加到布局文件的根视图上.
4.0 packages/apps/InCallUI/res/layout/call_card_content.xml
来电界面的布局xml文件是在call_card_content.xml进行,在这里没有
绿色的来电信心,号码/归属地/等等 ….: primary_call_info_container
下方4个按键的接听/拒接/短信 : @+id/answerFragment
我们知道,在布局过程中,有静态布局和动态布局这两种方式,
setContentView()
LayoutInflater.inflate()
这两种方式,这两种方式的主要特点在上面已经介绍过。
既然我们知道,那么我们就看看这是是怎么进行调用的,通过全局搜索我们可以看到
packages/apps/InCallUI/src/com/android/incallui/CallCardFragment.java
中对call_card_content进行了引用,
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mDensity = getResources().getDisplayMetrics().density;
mTranslationOffset =
getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset);
**return inflater.inflate(R.layout.**call_card_content**, container, false)**;
}
在这里我们看到,onCreateView动态加载了call_card_content.xml这个文件,在这里进行了初始化。
那么我们如果往上再看的话,又是哪里来调用这个CallCardFragment.java的呢,
我们来搜下“CallCardFragment”这个字串,我们可以看到,
比较有价值的地方是这个:
packages/apps/InCallUI/res/layout/incall_screen.xml:
我们这里采用的是从下往上逆向查找的方法来进行源码分析,这样的方法更直接直观的去寻找我们需要的东西,如果我们顺着源码的执行顺序来查找,那么我们一个个的去看每个文件,这样浪费的时间就比较多了。
到了这里,我们就对哪里调用的incall_screen.xml这个文件比较熟悉了,我们回到了
InCallActivity.java
5.0 当前界面来电的不同处理
我们知道,5.1的来电界面解锁前是直接进入到来电界面的,但是如果是在解锁后的某一个界面中,这个时候来电,就会现在状态栏提示有来电,点击之后才进入到来电界面。那么我们怎么来查找这个判断是否在解锁前或者后的界面在哪里呢?我们应该从哪里入手比较合理呢?
5.1 查找incallactivity的调用
我们知道,启动一个activity的方法,一般会在AndroidManifest.XML里面有相关的属性能进行查找,比如
Intent / resevice …..
还有一个就是我们在代码中动态的启动,那么既然是这样,启动一个intent我们知道,必须对包名和类型进行定义。那么我们先来看下incallactivity.java在AndroidManifest.xml中的定义是怎样的
<activity android:name="com.android.incallui.InCallActivity"
android:theme="@style/Theme.InCallScreen"
android:label="@string/phoneAppLabel"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:configChanges="keyboardHidden"
android:exported="false"
android:screenOrientation="nosensor" >
</activity>
在这里,我们能很清楚的看到,这里没有intent/action/receiver/service……..
等等这下关键得词,既然在这里我们得不到启动该activity的有用信息,那么我们就换个思路去找找看,我们从动态启动该activity去想想看到底这个是怎么启动的。我们知道5.1之后一个intent的启动必须要同时对包名和类名进行注册,但是,包名不是唯一的,但是类名一定是唯一的,所以,我们先从类名入手,我们在InCallUi这个模块下去搜索用到“InCallActivity”这个类名的地方,我们来看搜索的结果
**xxxxx@build-server-C:xxxxx grep−r“InCallActivity.class”packages/app