安卓来电秀简单实现

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

请不要在意这些细节;

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

需要运用的知识:广播、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中进行注册。

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

时间: 2024-08-25 00:12:55

安卓来电秀简单实现的相关文章

深圳市安卓工控设备有限公司简单介绍

深圳市安卓工控设备有限公司成立于二〇一三年,公司提供专业的行业定制化产品解决方式(基于安卓平台深度定制化解决方式和物联网工业控制解决方式).公司自主研发了KK系列安卓工控主板以及KC系列物联网控制模块等产品. 安卓平台深度定制化解决方式之硬件方案 安卓工控基于嵌入式ARM安卓平台技术,提供专业的软硬件产品和行业定制化解决方式.产品包含工业多点电容式人机界面(HMI),安卓行业平板(可支持NFC,ZIGBEE,3G,GPS等),工控机顶盒,工业设备控制一体机.安卓工控主板等. 安卓平台深度定制化解

【安卓基础】简单快捷的加载中对话框

遇到的需求 项目中有些界面需要显示加载中,例如登陆界面.注册界面等等.一开始考虑找个第三方库,但是第三方库往往为了达到普遍的适用性,封装得非常复杂.有时候一个库就差不多1mb大小,这样接入成本太大了,况且一个项目还需要其他第三方库接入,如果每一个功能都用第三方库解决,势必导致开发出来的应用体积臃肿,而且难以管理结构. 而我只是需要简单展示一个加载中提示,所以自己实现一个会更加合理,而且更加小巧灵活. 方案的选择 在安卓开发中,系统提供了对话框类用于开发.所以我直接选择使用Support V7的A

安卓与HTML简单的交互使用

实现通过java代码与HTML的一个互相操作. 准备工作: 1.新建Android工程,在布局文件中添加WebView控件. 2.准备一个HTML文件,放在src/main/assets文件夹下. 3.在java中得到webview,通过loadUrl找到assets下的HTML文件,必须设置webview能使用js(settings.setJavaScriptEnabled(true))加载显示如下效果: 简单的效果实现 一.点击HTML上sayHello按钮,可以得到java代码返回的字符串

Bolts框架在安卓中的简单应用

在新公司的项目中,是由bolts框架来搭建网络请求的,由于之前没有接触过这个框架,因此只能从头看起(其实看下来相对来说我更喜欢用RxJava+Retrofit搭建apps的请求层,but,whatever...),下面的内容很多都是在网上看完资料后自己写Demo来验证获得的,部分是翻译GitHub的Bolts框架的说明文档,也有部分是我自己在网上的教程的基础上自己验证后的代码,如有雷同,纯属巧合. 1.简介 Bolts是一个用于简化移动app开发的轻量级函数库集合,是由Parse和Faceboo

安卓开发之简单的短信操作模块

最近做了一个简单的短信操作模块,比较实用小巧.主要功能是可以发送短信(包括短信发送状态的提示),储存短信(可以用于短信列表显示等等),短信的储存可以用SQLite,不过觉得也就几十条上百条短信,用SQLite未免大材小用,还麻烦,于是决定用SharePreference结合对象序列化来做这个模块. 首先是短信实体类,为了复用,做成抽象类,再具体项目中使用只要继承该抽象类即可: public abstract class Message implements Serializable{ priva

安卓计步器的简单实现

参考链接:https://blog.csdn.net/fjnu_se/article/details/90906209 大体思路:在参考链接的基础上,我去掉了显示时间的线程.用android自带计步传感器进行计步,用一个数据库存储每日0点时计步器的总步数,由此可以计算出当日的步数.如果手机重启过,就清除掉所有的旧记录.现有问题是只有在app运行的情况下才能把当日0点时计步器的总步数加入数据库. 运行效果图: 结构目录: activity_main.xml: 1 <?xml version="

简单好用的安卓程序开发平台E4A

E4A为安卓程序中文开发平台,开放平台搭建过程简单,安卓程序编写简单快速! E4A发布版需要收费,免费版则是附带广告的.(对于不是用于商业软件来说,使用免费版是可以的)

android源码大放送(实战开发必备),免费安卓demo源码,例子大全文件详细列表

免费安卓demo源码,例子大全文件详细列表 本列表源码永久免费下载地址:http://www.jiandaima.com/blog/android-demo 卷 yunpan 的文件夹 PATH 列表 卷序列号为 0000-73EC E:. │ jiandaima.com文件列表生成.bat │ 例子大全说明.txt │ 本例子永久更新地址~.url │ 目录列表2016.03.10更新.txt │ ├─前台界面 │ ├─3D标签云卡片热门 │ │ Android TagCloudView云标签

安卓开发(1)

第一天是安卓系统的简单介绍. 安卓由来:安卓的虚拟机是dalvak,安卓是基于linux开发的. 安卓的开发环境配置:使用eclipse开发.首先需要下载adt.然后打开eclipse中有一个sdk manage下载所需要的安卓版本. eclipse界面介绍:eclipse的主要界面包括两个部分,java界面和ddms界面,这两个在eclipse的右上角,点击后可以切换.ddms主要是关于虚拟机的状态,如果在device界面看到有设备的话,说明模拟器已经启动了,如果没有,回到java界面,然后点