手机卫士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是存放应用的一些权限信息的;
    • 判断是系统应用还是用户应用

      1. 解释标识左移几位的效果public static final int FLAG_SYSTEM = 1<<0;
      2. 不同标识可以加起来一起使用, 加起来的结果和特定标识进行与运算,通过计算结果可以知道具不具备该特定标识的相关功能, 这种方式叫做状态机
      3. 玩游戏距离: 喝药水,加功能(加血,加攻击力,加防御,加魔法值),可以通过状态机来表示该药水具备哪些特性
      4. 实际开发的机顶盒举例:{“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
原创粉丝点击