安卓来电秀简单实现
效果图[图有点浮夸,但是功能是一样的,效果差一点。。。]:
请不要在意这些细节;
功能描述:就是当数据库中存在的联系人打电话来的时候,在手机上显示一个界面提示相关号码的信息。
需要运用的知识:广播、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中进行注册。
来电显示的界面美化问题就需要后期好好的进行修改了!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。