360手机卫士里基本的功能–查询来电归属地
技术要点大概如下
- 对最新号码归属地数据的压缩与解压
- sqlite3数据库的基本操作
- 监听手机来电
- 自定义Toast
准备操作
最新号码归属地数据库,这里有一份但不是最新的https://pan.baidu.com/s/1bpKSghT,这是压缩过的2兆多,解压后40多兆,本来更大,这里将一个表拆成多表,减少数据冗余,其实里面的表还可以拆分,更大程度的减少数据冗余,只是查询上会造成单表查询变成多表查询,不过这种拆分应该是值得的。
1.解压
前面准备的数据库其实已经压缩过了,是用下面的工具类ZipUtil实现的,用360压缩也是ok的,亲测一样。压缩过的(链接中下载到的)放到assets目录下,启动应用程序的时候解压缩,或者用到的时候再解压,暂称为懒解压。工具类ZipUtil如下:
/** * 压缩 * @param filePath * @param savePath * @throws Exception */
public static void compress(String filePath,String savePath) throws Exception{
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
savePath));
BufferedOutputStream bos = new BufferedOutputStream(out);
File file = new File(filePath);
zip(out,file,file.getName(),bos);
if(bos != null){
bos.close();
}
if(out != null){
out.close();
}
}
private static int k = 0;
private static void zip(ZipOutputStream out,File f,String base,BufferedOutputStream bo) throws Exception { // 方法重载
if (f.isDirectory()) {
File[] fl = f.listFiles();
if (fl.length == 0) {
out.putNextEntry(new ZipEntry(base + "/")); // 创建zip压缩进入点base
System.out.println(base + "/");
}
for (int i = 0; i < fl.length; i++) {
zip(out,fl[i],base + "/" + fl[i].getName(),bo); // 递归遍历子文件夹
}
System.out.println("第" + k + "次递归");
k++;
} else {
out.putNextEntry(new ZipEntry(base)); // 创建zip压缩进入点base
System.out.println(base);
FileInputStream in = new FileInputStream(f);
BufferedInputStream bi = new BufferedInputStream(in);
int b;
while ((b = bi.read()) != -1) {
bo.write(b); // 将字节流写入当前zip目录
}
bi.close();
in.close(); // 输入流关闭
}
}
/** * 解压缩 * @param filePath 源文件路径 * @param rootPath 要解压到哪个文件夹下 * @throws Exception */
public static void uncompress(String filePath,String rootPath) throws Exception{
ZipInputStream zis=new ZipInputStream(new FileInputStream(
filePath));//输入源zip路径
BufferedInputStream bis=new BufferedInputStream(zis);
File fOut=null;
ZipEntry entry;
while((entry = zis.getNextEntry())!=null){
if(entry.isDirectory()){
continue;
}
fOut=new File(rootPath,entry.getName());
if(!fOut.exists()){
(new File(fOut.getParent())).mkdirs();
}
FileOutputStream out=new FileOutputStream(fOut);
BufferedOutputStream Bout=new BufferedOutputStream(out);
int b;
while((b=bis.read())!=-1){
Bout.write(b);
}
Bout.flush();
Bout.close();
out.close();
}
bis.close();
zis.close();
}
ok,解压完成,40多兆的东西进了手机,流汗!
2.监听来电,自定义toast
用后台开启服务的方式监听手机来电
public class MonitorPhoneService extends Service {
private static final String TAG = "MonitorPhoneService";
private WindowManager windowManager;
private TelephonyManager manager;
private MyListener listener;
private View view;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
listener = new MyListener();
manager.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);
}
private class MyListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state,String incomingNumber) {
super.onCallStateChanged(state,incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: //处于静止状态,没有呼叫
if (view != null) { //这里及时移除view(自定义toast)
windowManager.removeView(view);
view = null;
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //接通状态
if (view != null) { //同上
windowManager.removeView(view);
view = null;
}
break;
case TelephonyManager.CALL_STATE_RINGING: //铃响状态
showAddress(incomingNumber);
break;
}
}
}
//显示归属地,自定义Toast
private void showAddress(String incomingNumber) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.toast_query_address,null);
TextView tv = (TextView) view.findViewById(R.id.tv_toast_query_address);
//这里操作数据库查询号码归属地
tv.setText(new QueryAddressEngine(getApplicationContext()).queryAddress(incomingNumber));
tv.setTextSize(20);
windowManager.addView(view,params);
}
@Override
public void onDestroy() {
super.onDestroy();
manager.listen(listener,PhoneStateListener.LISTEN_NONE);
listener = null;
}
}
只需启动服务监听就行了,还要添加权限
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
public class QueryAddressEngine {
private Context mContext;
public QueryAddressEngine(Context mContext) {
this.mContext = mContext;
}
public String queryAddress(String number) {
//匹配手机号可以写成工具类
String pattern = "^1[3458]\\d{9}$";
String result = number;
int type = -1;
String city = "";
String province = "";
if(number.matches(pattern)){ //手机号码
sqliteDatabase database = DbDao.getDatabase(mContext);
if(database.isOpen()){
Cursor cursor = database.rawQuery(
"select province,city,type from phone where mobilepre = ? limit 1",new String[]{number.substring(0,7)});
if(cursor.moveToNext()){
city = cursor.getString(cursor.getColumnIndex("city"));
type = cursor.getInt(cursor.getColumnIndex("type"));
province = cursor.getString(cursor.getColumnIndex("province"));
}
result = province + " " + city + " " + getOperatorByType(type);
cursor.close();
database.close();
}
} else { //固定电话
int len = number.length();
sqliteDatabase db;
switch (len){
case 4: //模拟器
result = "模拟器";
break;
case 7: //本地号码
case 8: //本地号码
result = "本地号码";
break;
case 10: //3位区号 + 7位号码
db = DbDao.getDatabase(mContext);
if(db.isOpen()){
Cursor cursor = db.rawQuery(
"select province,type from phone where areacode = ? limit 1",3)});
if(cursor.moveToNext()){
city = cursor.getString(cursor.getColumnIndex("city"));
type = cursor.getInt(cursor.getColumnIndex("type"));
province = cursor.getString(cursor.getColumnIndex("province"));
}
result = province + " " + city + " " + getOperatorByType(type);
cursor.close();
db.close();
}
break;
case 11: //3位区号 + 8位号码 4位区号 + 7位号码
sqliteDatabase db2 = DbDao.getDatabase(mContext);
if(db2.isOpen()){
Cursor cursor = db2.rawQuery(
"select province,3)});
if(cursor.moveToNext()){
city = cursor.getString(cursor.getColumnIndex("city"));
type = cursor.getInt(cursor.getColumnIndex("type"));
province = cursor.getString(cursor.getColumnIndex("province"));
} else {
Cursor cursor2 = db2.rawQuery(
"select province,4)});
if(cursor.moveToNext()){
city = cursor.getString(cursor.getColumnIndex("city"));
type = cursor.getInt(cursor.getColumnIndex("type"));
province = cursor.getString(cursor.getColumnIndex("province"));
}
cursor2.close();
}
result = province + " " + city + " " + getOperatorByType(type);
cursor.close();
db2.close();
}
break;
case 12: //4位区号 +8位号码
db = DbDao.getDatabase(mContext);
if (db.isOpen()) {
Cursor cursor = db.rawQuery(
"select province,new String[] { number.substring(0,4) });
if(cursor.moveToNext()){
city = cursor.getString(cursor.getColumnIndex("city"));
type = cursor.getInt(cursor.getColumnIndex("type"));
province = cursor.getString(cursor.getColumnIndex("province"));
}
result = province + " " + city + " " + getOperatorByType(type);
cursor.close();
db.close();
}
break;
}
}
return result;
}
public String getOperatorByType(int type){
String operator = "未知";
switch (type){
case 1:
operator = "移动";
break;
case 2:
operator = "联通";
break;
case 3:
operator = "电信";
break;
case 4:
operator = "虚拟运营商";
break;
}
return operator;
}
}
public class DbDao {
public static sqliteDatabase getDatabase(Context mContext){
return new DbHelper(mContext).getWritableDatabase();
}
}
public class DbHelper extends sqliteOpenHelper {
public DbHelper(Context context){
this(context,Db.NAME,null,Db.VERSION);
}
public DbHelper(Context context,String name,sqliteDatabase.CursorFactory factory,int version) {
super(context,name,factory,version);
}
@Override
public void onCreate(sqliteDatabase db) {
}
@Override
public void onUpgrade(sqliteDatabase db,int oldVersion,int newVersion) {
}
}
好了,写完收工领盒饭