安卓来电秀简单实现

效果图[图有点浮夸,但是功能是一样的,效果差一点。。。]:


技术分享

请不要在意这些细节;


功能描述:就是当数据库中存在的联系人打电话来的时候,在手机上显示一个界面提示相关号码的信息。

 

需要运用的知识:广播、Service、浮动窗口、数据库

实现的逻辑:用广播来实现监听来电的功能,在Service中调用数据库中的联系人信息,判断来电号码是否在数据库中,然后决定是否显示【来电秀】

注意:广播、Service的部分权限需要在AndroidMainfest.xml文件中注册,为了达到更好的广播监听效果:广播的注册页时在AndroidMainfest.xml文件中注册,也可以在代码中动态注册。这里我们选择前者,它的有点是即时App退出后,广播的监听也依然存在。而后者不是如此。来电显示的UI其实是一个浮动窗口,我们需要在layout文件夹中编写一个显示的界面,本文中为其命名为:float_layout_callerid.xml

 

 

下面直接上代码了,比较主要的部分将用彩色背景标出:

 

我们先给出在AndroidMainfest.xml文件中需要加入的代码:

 

<!-- 来电秀关联权限 -->

<uses-permissionandroid:name="android.permission.READ_PHONE_STATE" />

<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW" />

 

<!-- 来电Service -->

        <serviceandroid:name="com.sdjxd.xmcbgl.callerid.ServiceCallerShow"></service>

        <!-- 来电监听广播接收器注册-->

        <receiverandroid:name="com.sdjxd.xmcbgl.callerid.BroadcastReceiverCaller">

            <intent-filterandroid:priority="1000">

                <actionandroid:name="android.intent.action.PHONE_STATE"/>

            </intent-filter>

        </receiver>

 

然后是浮动窗口的界面文件:

<?xmlversion="1.0" encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

   android:layout_width="wrap_content"

   android:layout_height="wrap_content"

    android:orientation="vertical"

   android:gravity="center_horizontal" >

      

    <ImageView

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

       android:layout_gravity="center_horizontal"

        android:src="@drawable/icon"

        />

    <TextView

        android:id="@+id/tv_name"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

              android:text="姓名"

        />

    <TextView

        android:id="@+id/tv_ssbm"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

              android:text="部门"

        />

    <TextView

        android:id="@+id/tv_qq"

        android:layout_width="wrap_content"

       android:layout_height="wrap_content"

              android:text="QQ"

        />

    <TextView

        android:id="@+id/tv_email"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

              android:text="邮箱"

        />

    <TextView

        android:id="@+id/tv_gh"

       android:layout_width="wrap_content"

       android:layout_height="wrap_content"

              android:text="工号"

        />

    <Button

        android:id="@+id/float_id"

        android:layout_width="wrap_content"

       android:layout_height="wrap_content"

        android:text="这是悬浮窗口中的一个按钮,这个按钮很纯洁!" />

   

</LinearLayout>

 

下面是广播文件

 

importandroid.content.BroadcastReceiver;

importandroid.content.Context;

importandroid.content.Intent;

importandroid.os.SystemClock;

importandroid.telephony.TelephonyManager;

/**

 *

 * @author kenshin

 * @explain 用于监听来电、接通、去电的广播。

 * @detail 通过来电号码判断是否调用公司的【来电显示[悬浮窗口]】/【来电显示[悬浮窗口]】这部分功能在Service中完成

 *                   该广播接收器的注册是在AndroidMainifest.xml文件中静态注册的

 */

publicclass BroadcastReceiverCaller extends BroadcastReceiver

{

       private Intent intent2 = null;

       public final static String B_PHONE_STATE= TelephonyManager.ACTION_PHONE_STATE_CHANGED;

      

       @Override 

       public void onReceive(Context context,Intent intent)

       { 

              //获取来电是的电话号码

              String phoneNumber =intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);

          String action = intent.getAction(); 

          //呼入电话 

          if(action.equals(B_PHONE_STATE))

          { 

                     intent2 = newIntent(context, ServiceCallerShow.class);

                     //将需要的信息赋给Service中的两个静态成员

                     ServiceCallerShow.context =context;

                     ServiceCallerShow.phoneNumBerStr= phoneNumber;

                    

                     SystemClock.sleep(500);// 睡0.5秒是为了让悬浮窗显示在360或别的悬浮窗口的上方

                     context.startService(intent2);//打开Service

                     doReceivePhone(context,intent);//进行细致监听,[来电ing][听话ing][挂断]

          } 

       }//onReceive

            

       /**

       * 处理电话广播.

       * @param context

       * @param intent

       */ 

    public void doReceivePhone(Context context,Intent intent)

    { 

              //获取来电时的状态:[来电ing][接通ing][挂断]

              TelephonyManager telephony =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); 

              intstate = telephony.getCallState();

              switch(state)

              {

                     caseTelephonyManager.CALL_STATE_RINGING://等待接电话

                          break; 

                     caseTelephonyManager.CALL_STATE_IDLE://电话挂断

                          context.stopService(intent2);//关闭Service中的悬浮窗口【关闭来电显示】

                          //貌似用mWindowManager.removeView(mFloatLayout);比stopService快?但是还没测试过

                          break;

                     caseTelephonyManager.CALL_STATE_OFFHOOK://通话中

                          context.stopService(intent2);

                          break;

               } 

     }//doReceivePhone处理电话广播[来电ing][接通ing][挂断]

}

Service类文件

 

importjava.util.HashMap;

importjava.util.List;

 

importcom.sdjxd.hussar.android.xmcbgl.R;

 

importandroid.app.Service;

importandroid.content.Context;

importandroid.content.Intent;

importandroid.graphics.PixelFormat;

importandroid.os.IBinder;

importandroid.view.Gravity;

importandroid.view.LayoutInflater;

importandroid.view.View;

importandroid.view.WindowManager;

importandroid.view.View.OnClickListener;

importandroid.view.WindowManager.LayoutParams;

importandroid.widget.Button;

importandroid.widget.LinearLayout;

importandroid.widget.TextView;

 

 

/**

 * @author kenshin

 * @explain 实现来电显示的功能

 * @detail 实现悬浮窗口,并将通讯录中对应人员的详细信息显示在悬浮窗口内的UI上,

 *        悬浮窗口——用到了一个布局文件:float_layout_callerid

 *        权限[2个]——需要在AndroidMainifest.xml中注册service的权限 和 系统窗口权限

 */

publicclass ServiceCallerShow extends Service

{

       public static Context context = null;

       public static String phoneNumBerStr =null;

       HashMap<String, String>detailInfoMap;

       //定义浮动窗口布局

    private LinearLayout mFloatLayout;

    private WindowManager.LayoutParamswmParams;

    //创建浮动窗口设置布局参数的对象

       private WindowManager mWindowManager;

       private Button mFloatView;

       private TextView tvName;

       private TextView tvBm;

       private TextView tvGh;

       private TextView tvEmail;

       private TextView tvQQ;

       private DBManager dbManager = null;

       @Override

       public void onCreate()

       {

              super.onCreate();

              if(context != null)

              {

                 if(phoneNumBerStr != null&& !phoneNumBerStr.equals(""))

                 {

                        dbManager = newDBManager(context);

                        if(dbManager.isComeFromCompany(phoneNumBerStr))

                        {

                               detailInfoMap= dbManager.getIdDetailInfo(phoneNumBerStr);

                               if(detailInfoMap!= null && detailInfoMap.size() > 0)

                               {

                                      //将来电号码的详细信息赋给悬浮窗中的对应UI

                                      createFloatView();

                               }

//                              else//当获取不到详细信息的时候,此时至少证明该用户是公司人员了,虽然不太可能走到这里。

//                              {

//                                     //仅仅显示是公司的员工即可

//                              }

                        }

                 }

              }

       }

 

       @Override

       public IBinder onBind(Intent intent)

       {

              // TODO Auto-generated method stub

              return null;

       }

       /**

        * 创建悬浮窗

        */

       private void createFloatView()

       {

              wmParams = newWindowManager.LayoutParams();

              //获取WindowManagerImpl.CompatModeWrapper

              mWindowManager =(WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE);

              //设置window type[优先级]

              wmParams.type = LayoutParams.TYPE_SYSTEM_ERROR;//窗口的type类型决定了它的优先级,优先级越高显示越在顶层

              //设置图片格式,效果为背景透明

        wmParams.format =PixelFormat.RGBA_8888;

        //设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)

        wmParams.flags =LayoutParams.FLAG_NOT_FOCUSABLE;

       

        //调整悬浮窗显示的停靠位置为顶部水平居中

        wmParams.gravity =Gravity.CENTER_HORIZONTAL | Gravity.TOP;

       

        // 以屏幕左上角为原点,设置x、y初始值

        wmParams.x = 0;

        wmParams.y = 0;

 

        /*// 设置悬浮窗口长宽数据

        wmParams.width = 200;

        wmParams.height = 80;*/

       

        //设置悬浮窗口长宽数据 

        wmParams.width =WindowManager.LayoutParams.WRAP_CONTENT;

        wmParams.height =WindowManager.LayoutParams.WRAP_CONTENT;

       

        LayoutInflater inflater =LayoutInflater.from(getApplication());

        //获取浮动窗口视图所在布局

        mFloatLayout = (LinearLayout)inflater.inflate(R.layout.float_layout_callerid, null);

        //添加mFloatLayout

        mWindowManager.addView(mFloatLayout,wmParams);

       

        //浮动窗口按钮

        mFloatView =(Button)mFloatLayout.findViewById(R.id.float_id);

        //【详细信息赋值 给UI 】++++++++++++

        tvName =(TextView)mFloatLayout.findViewById(R.id.tv_name);

        tvBm =(TextView)mFloatLayout.findViewById(R.id.tv_ssbm);

        tvGh =(TextView)mFloatLayout.findViewById(R.id.tv_gh);

        tvEmail =(TextView)mFloatLayout.findViewById(R.id.tv_email);

        tvQQ =(TextView)mFloatLayout.findViewById(R.id.tv_qq);

        String nameStr =detailInfoMap.get("XM");

        String bmStr =detailInfoMap.get("SSBM");

        String ghStr =detailInfoMap.get("GH");

        String qqStr =detailInfoMap.get("QQ");

        String emainlStr =detailInfoMap.get("EMAIL");

        //有值就赋上,没值就不显示该UI

        if(!nameStr.equals(""))

        {

               tvName.setText(nameStr);

        }

        else

        {

               tvName.setVisibility(View.GONE);

        }

        if(!bmStr.equals(""))

        {

               tvBm.setText(bmStr);

        }

        else

        {

               tvBm.setVisibility(View.GONE);

        }

        if(!ghStr.equals(""))

        {

               tvGh.setText(ghStr);

        }

        else

        {

               tvGh.setVisibility(View.GONE);

        }

        if(!qqStr.equals(""))

        {

               tvQQ.setText(qqStr);

        }

        else

        {

               tvQQ.setVisibility(View.GONE);

        }

        if(!emainlStr.equals(""))

        {

               tvEmail.setText(emainlStr);

        }

        else

        {

               tvEmail.setVisibility(View.GONE);

        }

        //+++++++++++

//        mFloatView.setText(phoneNumBerStr);

       mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0,

                            View.MeasureSpec.UNSPECIFIED),View.MeasureSpec

                            .makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED));

       

        mFloatView.setOnClickListener(newOnClickListener()

        {

                     @Override

                     public void onClick(View v)

                     {

                            stopService(newIntent(v.getContext(), ServiceCallerShow.class));

                     }

              });//mFloatView.setOnClickListener[结束服务,关闭悬浮窗]

       

       }//createFloatView()//创建悬浮窗

      

       @Override

       public void onDestroy()//关闭悬浮窗

       {

              // TODO Auto-generated method stub

              super.onDestroy();

              if(mFloatLayout != null)

              {

                     mWindowManager.removeView(mFloatLayout);

              }

       }//onDestroy()//关闭悬浮窗[当该service停止的时候也执行该方法]

}

 

用到的关于对数据库操作的文件

packagecom.sdjxd.xmcbgl.callerid;

 

importjava.util.ArrayList;

importjava.util.HashMap;

importjava.util.List;

 

importandroid.content.Context;

importandroid.database.Cursor;

importandroid.database.sqlite.SQLiteDatabase;

importandroid.os.Environment;

 

importcom.sdjxd.hussar.android.utils.DataBaseHelper;

importcom.sdjxd.hussar.android.xmcbgl.R;

 

/**

 *

 * @author kenshin

 * @isComeFromCompany 判断号码是否是来自公司

 * @getIdDetailInfo 获取参数号码的详细信息

 */

publicclass DBManager

{

       private DataBaseHelper dbHelper;

      

       public DBManager(Context context)

       {

              String path = "";

              if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))

              {

                     String appName =context.getString(R.string.app_name);

                     path =Environment.getExternalStorageDirectory().getAbsolutePath() +"/data/" + appName + "/";

              }

              else

              {

                     path =context.getFilesDir().getAbsolutePath() + "/";

              }

              String dbPath = path +"mobileDataBase/";

              dbHelper = newDataBaseHelper(context, dbPath, "basedb", false);

       }

       /**

        *

        *@param phoneNumStr

        *@return 号码是否来自公司人员 是则返回true

        */

       public boolean isComeFromCompany(StringphoneNumStr)

       {    

              boolean flag = false;

              String sqlStr = "SELECT SJFROM CBHS_RYGL_RYXXXX WHERE SJ = ‘"+phoneNumStr+"‘";

              dbHelper.openDataBase();

              Cursor cs =dbHelper.queryData(sqlStr);

              if(cs != null)

              {

                     if (cs.moveToFirst())

                     {

                            flag =  true;

                     }

                     cs.close();

              }

              dbHelper.close();

              return flag;

       }

      

       /**

        *

        *@param callerId 来电显示的号码

        *@return 包含来电号码的详细信息的list

        */

       public HashMap<String, String>getIdDetailInfo(String callerId)

       {    

              HashMap<String, String>detailInfoMap = new HashMap<String, String>();

              //查询数据库看是否有数据,有数据就赋给List

              StringBuffer sql_xxxxStr = newStringBuffer();

              sql_xxxxStr.append("selectXM, GH, SJ, QQHM, YX,ORGANISENAME ");

              sql_xxxxStr.append("fromCBHS_RYGL_RYXXXX,JXD7_XT_ORGANISE ");

              sql_xxxxStr.append("where  SZBM = ORGANISEID and SJ =‘");

              sql_xxxxStr.append(callerId);

              sql_xxxxStr.append("‘");

             

              dbHelper.openDataBase();

              Cursor cs =dbHelper.queryData(sql_xxxxStr.toString());

              if(cs != null)

              {

                     if(cs.moveToFirst())

                     {

                            detailInfoMap.put("XM",cs.getString(cs.getColumnIndex("XM")));

                            detailInfoMap.put("SSBM",cs.getString(cs.getColumnIndex("ORGANISENAME")));

                            detailInfoMap.put("GH",cs.getString(cs.getColumnIndex("GH")));

                            detailInfoMap.put("QQ",cs.getString(cs.getColumnIndex("QQHM")));

                            detailInfoMap.put("EMAIL",cs.getString(cs.getColumnIndex("YX")));

                     }

                     cs.close();

              }

              dbHelper.close();

              return detailInfoMap;

       }

}

 

综上即可实现【来电秀的功能】

再次强调:需要注意的细节:

1耗时的操作(对数据库的操作等)不应放在广播中处理,而是放在Service中来进行处理。

2在浮动窗口的创建中,要特别注意浮动窗口的TYPE类型属性,这其实是窗口的优先级,该优先级的高低将直接影响到浮动窗口显示在首届界面上的“层次”,本文中选中的类型是ERROR类型,通过测试,这样的类型将显示在手机屏幕界面中的最顶层。

3广播的注册最好选择是在AndroidMainfest.xml文件中静态注册,有点就不赘述了。

4需要注意广播、service需要的一些权限也要在AndroidMainfest.xml中进行注册。

 

来电显示的界面美化问题就需要后期好好的进行修改了!



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。