上一篇文章写了使用WindowManager实现从自己的app界面拨打电话调用系统通话展示自定义布局。这一篇文章主要介绍一下使用BroadcastReceiver、Service配合WindowManager实现当有电话打进来时,搜索该号码是否是自己app内的用户(好友),显示特定的信息。
既然使用了BroadcastReceiver和Service,首先先介绍一下BroadcastReceiver和Service吧。
1.BroadcastReceiver
BroadcastReceiver顾名思义就是广播接收器,广播分为广播接收者和发送者,属于Android四大组件之一,用于监听 / 接收 应用 App 发出的广播消息,并 做出响应。
BroadcastReceiver的注册分为两种:静态注册和动态注册。
(一)静态注册:在清单文件中通过标签声明,同时声明要接收的action。
当此 App首次启动时,系统会自动实例化该BroadcastReceiver类,并注册到系统中,至此它的声明周期就和该app没什么关系了,android不能自动销毁广播接收器,也就是说当应用程序关闭后,还是会接收广播。
例如:
<receiver android:name=".PhoneReceive">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
<action android:name="com.ckw.CUSTOM_PHONE"/>
intent-filter>
receiver>
(二)动态注册:在代码中调用Context.registerReceiver()方法注册,这里不再多说。动态注册的广播接收器的声明周期和宿主的声明周期一致,当宿主被destroy了,它也就被销毁了。
2.Service
Service也是Android的四大组件之一,可以使用Service在后台执行耗时操作,例如下载。
Service的启动方式也分为两种
(一)startService:
其他组件通过调用startService方法,启动service。service会一直运行在后台,即使启动它的组件已经被销毁了。
例如:
Intent ringingIntent = new Intent(mContext,PhoneService.class);
ringingIntent.putExtra("phoneState",TelephonyManager.CALL_STATE_RINGING);
mContext.startService(ringingIntent);
这种方式需要在清单文件中注册服务:
<service android:name=".PhoneService"/>
(二)bindService:
其他组件调用bindService()方法绑定一个Service。多个组件可与一个service绑定,service不再与任何组件绑定时,该service会被destroy。
这种方式的service可以通过与Activity绑定,来实现与Activity的通信,这里不再多说。
3.功能实现:
这里用到了静态注册的BroadcastReceiver和startService方式创建的Service。
来电界面显示的具体流程如图:
来电之后,通过App内部的广播通过PhoneStateListener接收系统广播的Action,
接着开启Service,之后将App内需要展示的信息(通过本地读取或者网络请求拿到),
将这些信息传给Service。让Service创建WindowManager来绘制自定义的界面,并将信息显示到来电界面。
下面直接贴上代码吧:
(一):Service
public class PhoneService extends Service{
/**
* 用于在线程中创建或移除悬浮窗。
*/
private Handler handler = new Handler();
/**
* 定时器,定时进行检测当前应该创建还是移除悬浮窗。
*/
private Timer timer;
private int mPhoneState;//0 挂断 1 来电 2 通话中 3 主动拨号
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mPhoneState = intent.getIntExtra("phoneState",0);
String userName = "";
String userDep = "";
if(mPhoneState == 3 || mPhoneState == 1){//拨打电话 这里后面还需要加个 1的类型
userName = intent.getStringExtra("userName");
userDep = intent.getStringExtra("userDep");
}
// 开启定时器,每隔0.5秒刷新一次
if (timer == null) {
timer = new Timer();
timer.scheduleAtFixedRate(new RefreshTask(userName,userDep), 0, 500);
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
// Service被终止的同时也停止定时器继续运行
timer.cancel();
timer = null;
}
class RefreshTask extends TimerTask {
private String userName;
private String userDep;
public RefreshTask(String userName,String userDep) {
this.userName = userName;
this.userDep = userDep;
}
@Override
public void run() {
if(!isHome() && !PhoneWindowManager.isWindowShowing()){
handler.post(new Runnable() {
@Override
public void run() {
PhoneWindowManager.createBigWindow(getApplicationContext(),userName,userDep);
}
});
}
// 当前界面是桌面,且有悬浮窗显示,则移除悬浮窗。
if (isHome() || mPhoneState == 0) {
handler.post(new Runnable() {
@Override
public void run() {
PhoneWindowManager.removeBigWindow(getApplicationContext());
Intent intent = new Intent(getApplicationContext(), PhoneService.class);
getApplicationContext().stopService(intent);
}
});
}
}
}
/**
* 判断当前界面是否是桌面
*/
private boolean isHome() {
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List rti = mActivityManager.getRunningTasks(1);
return getHomes().contains(rti.get(0).topActivity.getPackageName());
}
/**
* 获得属于桌面的应用的应用包名称
*
* @return 返回包含所有包名的字符串列表
*/
private List getHomes() {
List names = new ArrayList();
PackageManager packageManager = this.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List resolveInfo = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo ri : resolveInfo) {
names.add(ri.activityInfo.packageName);
}
return names;
}
}
(二)BroadcastReceiver:
public class PhoneReceive extends BroadcastReceiver implements PhoneInfoContract.View{
private Context mContext;
@Inject
PersonalAffairsApi personalAffairsApi;
@Inject
HttpManager httpManager;
@Override
public void onReceive(Context context, Intent intent) {
mContext = context;
if (intent.getAction().equals("com.ckw.CUSTOM_PHONE")) {//从自己的程序里发出的,拨打电话
String userName = intent.getStringExtra("userName");
String userDep = intent.getStringExtra("userDep");
Intent phoneIntent = new Intent(context, PhoneService.class);
phoneIntent.putExtra("userName", userName);
phoneIntent.putExtra("userDep", userDep);
phoneIntent.putExtra("phoneState", 3);
context.startService(phoneIntent);
}
//如果是去电
else if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
//主动拨打电话的话,会经过这里,但是我们用上面的代码,因为需要传自己的数据过来
} else {
//设置一个监听器
TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
PhoneStateListener listener = new PhoneStateListener(){
@Override
public void onCallStateChanged(int state, String incomingNumber) {
//注意,方法必须写在super方法后面,否则incomingNumber无法获取到值。
super.onCallStateChanged(state, incomingNumber);
switch(state){
case TelephonyManager.CALL_STATE_IDLE:
Intent phoneIntent = new Intent(mContext,PhoneService.class);
phoneIntent.putExtra("phoneState",TelephonyManager.CALL_STATE_IDLE);
mContext.startService(phoneIntent);
break;
case TelephonyManager.CALL_STATE_OFFHOOK://应该是打电话的状态,通话中
break;
case TelephonyManager.CALL_STATE_RINGING://响铃,比如来电
String userId = DbHelper.getUserId(mContext);
PhoneInfoPresenter phoneInfoPresenter = new PhoneInfoPresenter(PhoneReceive.this,mContext);
phoneInfoPresenter.getPhoneInfo(incomingNumber,userId);
break;
}
}
};
@Override
public void showPhoneInfoResult(PhoneInfo info) {
//在这里开启service
Intent ringingIntent = new Intent(mContext,PhoneService.class);
ringingIntent.putExtra("userName",info.getXm());
ringingIntent.putExtra("userDep",info.getDepname());
ringingIntent.putExtra("phoneState",TelephonyManager.CALL_STATE_RINGING);
mContext.startService(ringingIntent);
}
@Override
public boolean isActive() {
return true;
}
@Override
public void showError(String msg) {
Log.d("----", "showError: "+msg);
}
@Override
public void setPresenter(PhoneInfoPresenter presenter) {
}
@Override
public void showProgressDialog(String msg) {
}
@Override
public void hideProgressDialog() {
}
}
(三)自定义的WindowManager:
public class PhoneWindowManager {
/**
* 显示的view
*/
private static FloatWindowView mFloatWindowView;
/**
* 悬浮窗View的参数
*/
private static WindowManager.LayoutParams bigWindowParams;
/**
* 用于控制在屏幕上添加或移除悬浮窗
*/
private static WindowManager mWindowManager;
/**
* 用于获取手机可用内存
*/
private static ActivityManager mActivityManager;
/**
* 创建一个悬浮窗。位置为屏幕正中间。
*
* @param context
* 必须为应用程序的Context.
*/
public static void createBigWindow(Context context,String userName,String userDep) {
WindowManager windowManager = getWindowManager(context);
if (mFloatWindowView == null) {
mFloatWindowView = new FloatWindowView(context,userName,userDep);
if (bigWindowParams == null) {
bigWindowParams = new WindowManager.LayoutParams();
bigWindowParams.x = 0;
bigWindowParams.y = 0;
bigWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE;
bigWindowParams.format = PixelFormat.RGBA_8888;
bigWindowParams.gravity = Gravity.CENTER;
bigWindowParams.height = FloatWindowView.viewHeight;
bigWindowParams.width = FloatWindowView.viewWidth;
}
windowManager.addView(mFloatWindowView, bigWindowParams);
}
}
/**
* 将悬浮窗从屏幕上移除。
*
* @param context
* 必须为应用程序的Context.
*/
public static void removeBigWindow(Context context) {
if (mFloatWindowView != null) {
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(mFloatWindowView);
mFloatWindowView = null;
}
}
/**
* 是否有悬浮窗显示在屏幕上。
*
* @return 有悬浮窗显示在桌面上返回true,没有的话返回false。
*/
public static boolean isWindowShowing() {
return mFloatWindowView != null;
}
/**
* 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。
*
* @param context
* 必须为应用程序的Context.
* @return WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。
*/
private static WindowManager getWindowManager(Context context) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
return mWindowManager;
}
/**
* 如果ActivityManager还未创建,则创建一个新的ActivityManager返回。否则返回当前已创建的ActivityManager。
*
* @param context
* 可传入应用程序上下文。
* @return ActivityManager的实例,用于获取手机可用内存。
*/
private static ActivityManager getActivityManager(Context context) {
if (mActivityManager == null) {
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
return mActivityManager;
}
/**
* 获取当前可用内存,返回数据以字节为单位。
*
* @param context
* 可传入应用程序上下文。
* @return 当前可用内存。
*/
private static long getAvailableMemory(Context context) {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
getActivityManager(context).getMemoryInfo(mi);
return mi.availMem;
}
}
(四)自定义的显示布局:
根据自己的需求来,这里就不贴了。