手机卫士day09
来源:互联网 发布:搜狗排名优化软件 编辑:程序博客网 时间:2024/06/05 08:48
day09
清除来电记录
代码挂断电话后,被挂断的号码仍然会进入通话记录中, 我们需要将这种记录删除. 查看数据库contacts2中的表calls /** * 删除通话记录 */ private void deleteCallLog(String number) { getContentResolver().delete(Uri.parse("content://call_log/calls"), "number=?", new String[] {number}); } 注意加权限: <uses-permission android:name="android.permission.READ_CALL_LOG"/> <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>- 通过内容观察者,解决通话记录删除失败的问题 系统在往通话记录的数据库中插入数据时是异步逻辑,所以当数据库还没来得及添加电话日志时,我们就执行了删除日志的操作,从而导致删除失败,为了避免这个问题,可以监听数据库变化,当数据库发生变化后,我们才执行删除操作,从而解决这个问题 /** * 内容观察者 * @author Kevin * */ class MyContentObserver extends ContentObserver { private String incomingNumber; public MyContentObserver(Handler handler, String incomingNumber) { super(handler); this.incomingNumber = incomingNumber; } /** * 当数据库发生变化时,回调此方法 */ @Override public void onChange(boolean selfChange) { System.out.println("call log changed..."); //删除日志 deleteCallLog(incomingNumber); //删除完日志后,注销内容观察者 getContentResolver().unregisterContentObserver(mObserver); } } ------------------------------ //监听到来电时,注册内容观察者 mObserver = new MyContentObserver(new Handler(), incomingNumber); //注册内容观察者 getContentResolver().registerContentObserver( Uri.parse("content://call_log/calls"), true, mObserver); ------------------------------ 注意: 补充Android2.3模拟器上需要多加权限 <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
短信备份
查看短信数据库
data/data/com.android.provider.telephony/databases/mmssms.dbaddress 短信收件人发件人地址date 短信接收的时间type 1 发进来短信 2 发出去短信read 1 已读短信 0 未读短信body 短信内容
读取短信数据库内容
查看系统源码,找到uri地址:packages\provider\platform_packages_providers_telephonyprovider-masterUri uri = Uri.parse("content://sms/");// 所有短信Cursor cursor = ctx.getContentResolver().query(uri, new String[] { "address", "date", "type", "body" }, null, null, null);遍历cursor,获取短信信息注意权限: <uses-permission android:name="android.permission.READ_SMS"/><uses-permission android:name="android.permission.WRITE_SMS"/>
将短信内容序列化为xml文件
sms.xml<?xml version="1.0" encoding="utf-8"?><smss> <sms> <address>5556</address> <date>10499949433</date> <type>1</type> <body>wos shi haoren</body> </sms> <sms> <address>13512345678</address> <date>1049994889433</date> <type>2</type> <body>hell world hei ma</body> </sms></smss>------------------------------XmlSerializer serializer = Xml.newSerializer();// 初始化xml序列化工具serializer.setOutput(new FileOutputStream(output), "utf-8");//设置输出流/* * startDocument(String encoding, Boolean standalone)encoding代表编码方式 * standalone 用来表示该文件是否关联其它外部的约束文件。 若值是 ”yes” 表示没有关联外部规则文件,若值是 ”no” * 则表示有关联外部规则文件。默认值是 “yes”。 * <?xml version="1.0" encoding="utf-8" standalone=true ?> * * 参数2传null时,不会生成standalone=true/false的语句 */serializer.startDocument("utf-8", null);// 生成xml顶栏描述语句<?xml // version="1.0" // encoding="utf-8"?>serializer.startTag(null, "smss");//起始标签serializer.text(body);// 设置内容serializer.endTag(null, "smss");//结束标签serializer.endDocument();//结束xml文档------------------------------AToolsActivity.java/** * 短信备份 */public void smsBackup(View view) { if (Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { try { SmsUtils.smsBackup(this, new File(Environment.getExternalStorageDirectory(), "sms.xml")); Toast.makeText(this, "备份成功!", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "备份失败!", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "没有检测到sdcard!", Toast.LENGTH_SHORT).show(); }}
异步备份短信,并显示进度条
mProgressDialog = new ProgressDialog(this);mProgressDialog.setMessage("正在备份短信...");mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//设置显示风格,此风格将展示一个进度条mProgressDialog.show();将ProgressDialog的引用传递给工具类,在工具类中更新进度SmsUtils.smsBackup(AToolsActivity.this, new File( Environment.getExternalStorageDirectory(), "sms.xml"), mProgressDialog);--------------------------------//短信工具类中更新进度条的逻辑progressDialog.setMax(cursor.getCount());// 设置进度条最大值Thread.sleep(500);//为了方便看效果,故意延时1秒钟progress++;progressDialog.setProgress(progress);//更新进度--------------------------------模拟需求变动的情况1. A负责短信备份界面, B负责短信工具类2. 将ProgressDialog改动为ProgressBar, 需要A通知B改动3. 又将ProgressBar改回ProgressDialog, 需要A通知B改动4. 既有ProgressBar,又要求有ProgressDialog, 需要A通知B改动问题: B除了负责底层业务逻辑之外,额外还需要帮A处理界面逻辑,如何实现A和B的解耦?
使用回调接口通知进度,优化代码,实现解耦
/** * 短信备份回调接口 * @author Kevin * */public interface SmsBackupCallback { /** * 备份之前获取短信总数 * @param total */ public void preSmsBackup(int total); /** * 备份过程中实时获取备份进度 * @param progress */ public void onSmsBackup(int progress);}----------------------------SmsUtils.smsBackup(AToolsActivity.this, new File( Environment.getExternalStorageDirectory(), "sms.xml"), new SmsBackupCallback() { @Override public void preSmsBackup(int total) { mProgressDialog.setMax(total); } @Override public void onSmsBackup(int progress) { mProgressDialog.setProgress(progress); } });
短信还原(介绍)
应用管理器(AppManagerActivity)
- 介绍金山卫士的应用管理器
- 参考金山卫士,编写布局文件
计算内置存储空间和sdcard剩余空间
/** * 获取剩余空间 * * @param path * @return */private String getAvailSpace(String path) { StatFs stat = new StatFs(path); // Integer.MAX_VALUE; // int最大只能表示到2G, 在一些高端手机上不足够接收大于2G的容量,所以可以用long来接收, 相乘的结果仍是long类型 long blocks = stat.getAvailableBlocks();// 获取可用的存储块个数 long blockSize = stat.getBlockSize();// 获取每一块的大小 return Formatter.formatFileSize(this, blocks * blockSize);// 将字节转化为带有容量单位的字符串}//获取内存的地址Environment.getDataDirectory().getAbsolutePath()//获取sdcard的地址Environment.getExternalStorageDirectory().getAbsolutePath()
获取已安装的应用列表
/** * 应用信息封装 * * @author Kevin * */public class AppInfo { public String name;// 名称 public String packageName;// 包名 public Drawable icon;// 图标 public boolean isUserApp;// 是否是用户程序 public boolean isRom;// 是否安装在内置存储器中 @Override public String toString() { return "AppInfo [name=" + name + ", packageName=" + packageName + "]"; }}-------------------------------AppInfoProvider.java/** * 获取已安装的应用信息 * @param ctx */public static ArrayList<AppInfo> getAppInfos(Context ctx) { ArrayList<AppInfo> infoList = new ArrayList<AppInfo>(); PackageManager pm = ctx.getPackageManager(); List<PackageInfo> packages = pm.getInstalledPackages(0);// 获取已经安装的所有包 for (PackageInfo packageInfo : packages) { AppInfo info = new AppInfo(); String packageName = packageInfo.packageName;// 获取包名 Drawable icon = packageInfo.applicationInfo.loadIcon(pm);// 获取图标 String name = packageInfo.applicationInfo.loadLabel(pm).toString();// 获取名称 info.packageName = packageName; info.icon = icon; info.name = name; infoList.add(info); } return infoList;}进行单元测试,打印应用列表
Android的应用程序安装位置
pc电脑默认安装在C:\Program FilesAndroid 的应用安装在哪里呢,如果是用户程序,安装在data/app/目录下,系统自带应用安装在system/app/目录下安装Android软件 做两件事 A:把APK拷贝到data/app/目录下 B:把安装包信息写到data/system/目录下两个文件packages.list 和 packages.xml安装包信息在data/system/Packages.list 里面的0 表示系统应用 1 表示用户应用Packages.xml是存放应用的一些权限信息的;
判断是系统应用还是用户应用
- 解释标识左移几位的效果public static final int FLAG_SYSTEM = 1<<0;
- 不同标识可以加起来一起使用, 加起来的结果和特定标识进行与运算,通过计算结果可以知道具不具备该特定标识的相关功能, 这种方式叫做状态机
- 玩游戏距离: 喝药水,加功能(加血,加攻击力,加防御,加魔法值),可以通过状态机来表示该药水具备哪些特性
实际开发的机顶盒举例:{“cctv1”:true,”cctv2”:false,”cctv3”:true}->{“flag”:101}, 可以节省流量
if ((flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) { info.isUserApp = false;// 系统应用} else { info.isUserApp = true;//用户应用}if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == ApplicationInfo.FLAG_EXTERNAL_STORAGE) { info.isRom = false;// 安装位置是sdcard} else { info.isRom = true;//安装位置是内置存储器}
ListView列表展现
- 仿照金山卫士编写item布局
异步加载应用列表数据,并显示进度条
加载应用列表有时会比较耗时,最好放在子线程执行. 加载期间显示加载中的布局 <FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent" ><ListView android:id="@+id/lv_list" android:layout_width="match_parent" android:layout_height="match_parent" ></ListView><LinearLayout android:id="@+id/ll_loading" android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:paddingBottom="50dp" android:orientation="vertical" > <ProgressBar android:id="@+id/progressBar1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="正在加载应用列表..." /></LinearLayout><TextView android:id="@+id/tv_head" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FF888888" android:textColor="#fff" android:text="用户程序(5)" />
清单文件中注册安装位置
manifest根标签具有这样的属性,用来指定apk的安装位置, 缺省值是手机内存 android:installLocation="auto", 可以修改为auto, 表示优先使用手机内存, 如果内存不够,再使用sdcard. 不建议强制安装在sdcard,因为某些手机没有sdcard,会导致安装失败.设置完成后,就可以在系统应用管理中,移动apk的安装位置了.
复杂ListView的展现方式
核心思想: 重写BaseAdapter自带的getItemViewType方法来返回item类型,重写getViewTypeCount方法返回类型个数//应用信息适配器class AppInfoAdapter extends BaseAdapter { /** * 返回总数量,包括两个标题栏 */ @Override public int getCount() { return 1 + mUserAppList.size() + 1 + mSystemAppList.size(); } /** * 返回当前的对象 */ @Override public AppInfo getItem(int position) { if (position == 0 || position == 1 + mUserAppList.size()) {//判断是否是标题栏 return null; } AppInfo appInfo; if (position < mUserAppList.size() + 1) {//判断是否是用户应用 appInfo = mUserAppList.get(position - 1); } else {//系统应用 appInfo = mSystemAppList .get(position - mUserAppList.size() - 2); } return appInfo; } @Override public long getItemId(int position) { return position; } /** * 返回当前view的类型 */ @Override public int getItemViewType(int position) { if (position == 0 || position == 1 + mUserAppList.size()) { return 1;// 标题栏 } else { return 0;// 应用信息 } } /** * 返回当前item的类型个数 */ @Override public int getViewTypeCount() { return 2; } @Override public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position);//获取item的类型 if (convertView == null) {//初始化convertView switch (type) { case 1://标题 convertView = new TextView(AppManagerActivity.this); ((TextView) convertView).setTextColor(Color.WHITE); ((TextView) convertView).setBackgroundColor(Color.GRAY); break; case 0://应用信息 ViewHolder holder; convertView = View.inflate(AppManagerActivity.this, R.layout.list_appinfo_item, null); holder = new ViewHolder(); holder.ivIcon = (ImageView) convertView .findViewById(R.id.iv_icon); holder.tvName = (TextView) convertView .findViewById(R.id.tv_name); holder.tvLocation = (TextView) convertView .findViewById(R.id.tv_location); convertView.setTag(holder); break; default: break; } } //根据类型来更新view的显示内容 switch (type) { case 1: if (position == 0) { ((TextView) convertView).setText("用户程序(" + mUserAppList.size() + ")"); } else { ((TextView) convertView).setText("系统程序(" + mUserAppList.size() + ")"); } break; case 0: ViewHolder holder = (ViewHolder) convertView.getTag(); AppInfo appInfo = getItem(position); holder.ivIcon.setImageDrawable(appInfo.icon); holder.tvName.setText(appInfo.name); if (appInfo.isRom) { holder.tvLocation.setText("手机内存"); } else { holder.tvLocation.setText("外置存储卡"); } break; default: break; } return convertView; }}
PopupWindow使用
专门写一个Demo,用于PopupWindow的演示/** * 显示弹窗 * * @param view */public void showPopupWindow(View view) { TextView contentView = new TextView(this); contentView.setText("我是弹窗哦!"); contentView.setTextColor(Color.RED); PopupWindow popup = new PopupWindow(contentView, 100, 100, true);//设置尺寸及获取焦点 popup.setBackgroundDrawable(new ColorDrawable(Color.BLUE));//设置背景颜色 // popup.showAtLocation(rlRoot, Gravity.LEFT + Gravity.TOP, 0, // 0);//显示在屏幕的位置 popup.showAsDropDown(btnPop, 0, 0);// 显示在某个控件的正下方}
将PopupWindow应用到项目当中
//listview监听lvList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { mCurrentAppInfo = mAdapter.getItem(position); System.out.println(mCurrentAppInfo.name + "被点击!"); showPopupWindow(view); }});-----------------------------------/** * 显示弹窗 */private void showPopupWindow(View view) { View contentView = View.inflate(this, R.layout.popup_appinfo, null); TextView tvUninstall = (TextView) contentView .findViewById(R.id.tv_uninstall); TextView tvLaunch = (TextView) contentView.findViewById(R.id.tv_launch); TextView tvShare = (TextView) contentView.findViewById(R.id.tv_share); //设置监听事件 tvUninstall.setOnClickListener(this); tvLaunch.setOnClickListener(this); tvShare.setOnClickListener(this); //初始化PopupWindow PopupWindow popup = new PopupWindow(contentView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true); popup.setBackgroundDrawable(new ColorDrawable());// 必须设置背景,否则无法返回 popup.showAsDropDown(view, 50, -view.getHeight());// 显示弹窗 //渐变动画 AlphaAnimation alpha = new AlphaAnimation(0, 1); alpha.setDuration(500); alpha.setFillAfter(true); //缩放动画 ScaleAnimation scale = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0.5f); scale.setDuration(500); scale.setFillAfter(true); //动画集合 AnimationSet set = new AnimationSet(false); set.addAnimation(alpha); set.addAnimation(scale); //运行动画 contentView.startAnimation(set);}-----------------------------------@Overridepublic void onClick(View v) { if (mCurrentAppInfo == null) { return; } switch (v.getId()) { case R.id.tv_uninstall: System.out.println("卸载" + mCurrentAppInfo.name); break; case R.id.tv_launch: System.out.println("启动" + mCurrentAppInfo.name); break; case R.id.tv_share: System.out.println("分享" + mCurrentAppInfo.name); break; default: break; }}
卸载,启动和分享的逻辑
/** * 卸载 */private void uninstall() { if (mCurrentAppInfo.isUserApp) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_DELETE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse("package:" + mCurrentAppInfo.packageName)); startActivityForResult(intent, 0); } else { Toast.makeText(this, "无法卸载系统程序!", Toast.LENGTH_SHORT).show(); }}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { // 卸载成功后重新加载应用列表 // 此处不必判断resultCode是否是RESULT_OK, 因为4.1+系统即使卸载成功也始终返回RESULT_CANCEL loadAppInfos(); super.onActivityResult(requestCode, resultCode, data);}------------------------------------/** * 启动App */private void launchApp() { try { PackageManager pm = this.getPackageManager(); Intent intent = pm .getLaunchIntentForPackage(mCurrentAppInfo.packageName);// 获取应用入口的Intent startActivity(intent);// 启动应用 } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "无法启动该应用!", Toast.LENGTH_SHORT).show(); }}------------------------------------/** * 分享 此方法会呼起系统中所有支持文本分享的app列表 */private void shareApp() { Intent intent = new Intent(Intent.ACTION_SEND); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, "分享给你一个很好的应用哦! 下载地址: https://play.google.com/apps/details?id=" + mCurrentAppInfo.packageName); startActivity(intent);}
ListView分类栏常驻效果
//原理: 写一个TextView常驻在ListView顶栏, 样式和item中分类栏的样式完全一样. 监听ListView的滑动事件,动态修改TextView的内容//设置listview的滑动监听lvList.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { System.out.println("onScroll:" + firstVisibleItem); if (mUserAppList != null && mSystemAppList != null) { if (firstVisibleItem <= mUserAppList.size()) { tvListHead.setText("用户应用(" + mUserAppList.size() + ")"); } else { tvListHead.setText("系统应用(" + mSystemAppList.size() + ")"); } } }});
0 0
- 手机卫士day09
- 手机卫士
- android项目-手机卫士
- Android实战--手机卫士
- 手机卫士 第三天
- 手机卫士 第十天
- Android手机卫士
- 手机卫士 自定义土司
- 手机卫士-01
- 手机卫士-02
- 手机卫士-03
- 手机卫士-04
- 手机卫士-05
- 手机卫士-06
- 手机卫士-07
- 手机卫士-08
- 手机卫士-09
- 手机卫士-10
- Zookeeper系列(十三)Zookeeper开源客户端之Curator的事件监听丢失分析
- linux修改语言环境为中文
- OpenGL绘制教室,带你亲手编程浅识高大上的VR技术
- 关于打不开DDMS中data文件夹的问题
- 手机卫士day08
- 手机卫士day09
- 在MyEclipse中出现了The type java.io.ObjectInputStream cannot be resolved. It is indirectly referenced fro
- 【jzoj5102】【GDOI2017 day2】【小学生语文题】【动态规划】
- jQuery怎么输出选择器选择出来的$对象的html代码,包括自己
- 串口 来做 控制台
- 总结一种使用OpenCV进行双目校正的方法其流程和所用函数
- Hadoop 历史服务器获取作业运行信息
- Java编程思想笔记第十章(内部类)
- dubbo搭建