上一篇文章写了使用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。

来电界面显示的具体流程如图:

android接听电话 安卓系统接电话界面_ide

来电之后,通过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<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
        return getHomes().contains(rti.get(0).topActivity.getPackageName());
    }

    /**
     * 获得属于桌面的应用的应用包名称
     *
     * @return 返回包含所有包名的字符串列表
     */
    private List<String> getHomes() {
        List<String> names = new ArrayList<String>();
        PackageManager packageManager = this.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List<ResolveInfo> 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;
    }
}

(四)自定义的显示布局:
根据自己的需求来,这里就不贴了。