【android】仿360手机卫士的简易设计思路及源码

         笔者最近一直忙于满广州的跑,实习好难找好难找,博客也是有点久没去更新。仿360手机卫士的实现的目的更多的是出于对常用知识点的一个巩固吧,比较适合像我这种接触没多久的学习者在学习之余拿来练手保持写代码的感觉的的一个不错的小项目。


      涉及的技术:

     都是些非常常用的android常用开发,但涉及面比较广吧。(小编这边也谈不出啥新意,就简简单单举几个小例子,见谅见谅~!~)也是出于练手的目的吧,在写这个项目的时候也是用到了比较多的控件,有时一个问题也用到了不同的方法:比方说在解析服务器的xml文件即用到一般的json数据解析,也用到了gson数据解析数据(据说这个gson目前被大部分公司所使用,小编是否有点out了,哈哈);listview花样就更多了,除了常规的listview +viewholder小优化思路,有些地方也是用到单个类别头部固定的listview;有些地方也用到自定义listview实现侧拉删除,同时还可以给listview条目增加动画增加视觉效果。另外,在listview数据加载时,可以是传统的加载,还有用到分批加载、分页加载。(哈哈,还是蛮好玩的)其他细节如果大家有兴趣直接进代码小看一番也不错,当然也用到许多动画和自定义控件,在下面各个部分的功能介绍小编也会小提一下啦。


      功能介绍如下:

      文章有限,小编这边就不罗列代码。。。

      

     1、手机防盗

       功能描述:在主界面点击手机防盗按钮,弹出密码输入框(若为第一次进入则弹出设置密码框界面),密码的存储方式经过MD5加密后存储在SharePreference中,在验证完密码后跳装到手机防盗页面(若还未对设置防盗信息会跳转到手机防盗设置页面),那就先来看下手机防盗设置页面:设置安全号码、绑定sim卡、以及是否开启防盗设置。手机防盗页面对设置的这些信息进行展示以及提供用户修改按钮。防盗功能:(1)开机监听手机sim卡变化,如果变化向安全号码发送提醒短信;(2)在手机丢失的情况下通过远程短信控制实现:被盗手机清除数据、被盗手机定位、被盗手机锁屏设置屏保以及控制被盗手机播报报警音乐。


  技术实现:(1)检测sim卡变化:在手机防盗页面监听sim卡按钮变化,如果按钮开启如果在手机防盗和sim卡按钮开机情况下,开机广播BootCompleteReceiver会获取当前手机sim卡号,和原来保存的sim卡号进行比较(通过TelephonyManager),若不一样,通过SmsManager对象以及安全号码发送短信。(2)手机定位、手机数据清理、手机防盗以及手机报警音乐播放:通过短信监听广播,通过获取短息内容来与原先设置好的口令惊醒比较,数据清理和一键锁屏通过设备管理器DevicePolicyManager实现,手机定位通过一个service监听位置变化。(3)从手机联系人处选择号码:相当于去读写手机内置的数据库吧.

  

  部分功能展示



  2、通讯卫士

   功能描述:(1)、添加黑名单(两种方式:通过输入框输入方面;以及通过调用手机联系人方式);(2)、查询黑名单;(3)、拦截在黑名单中的短息以及电话。


   技术实现(1)添加黑名单:建立一个数据库表,对其进行封装,将黑名单号码保存在数据库中。(2)查询黑名单:由于没有找到合适的数据库,因此黑名单数据库就用刚刚建立的一个黑名单数据库来代替,查询数据库。(3)短信和电话拦截:(这个功能在设置中心进行设置,默认为开启状态--即直接在splash页面直接开启黑名单服务),通过活动管理器ActivityManager获取后台正在运行的服务,判定黑名单服务是否已经开启。1、电话拦截:通过电话管理器TeleponyManager监听手机电话状态,获取来电号码,查询数据库,如果黑名单数据库中存在该号码,通过反射方式获取系统拦截电话方法拦截电话,并且通过contentprovider方式去删除电话记录中的来电记录;2、短信拦截:在服务中动态的注册广播,通过SmsManager对象回去短信的号码,查询数据库,调用abortBroadcast来拦截短信。


 部分功能展示



 3、软件管理

  功能描述获取系统安装的所用应用,分类成手机应用和系统应用通过listview显示。通过点击每个条目的应用对该应用操作:运行、卸载、分享以及详细信息。


  技术实现:(1)、获取手机安卓的应用:通过系统提供的包管理器获取应用信息,(2)、运行、卸载、分享、详细信息:通过StartAcitvity()跳转到相对应的activity操作。


 部分功能展示



 4、进程管理

   功能描述获取系统正在运行的应用的详细信息,显示在listview中。并供用户选择单个条目的清理进程或全部清理。


   技术实现:通过包管理器PackageManager以及任务管理器获取系统正在运行的所用进程的详细信息,通过自定义listview展示(侧滑清理单个进程,一键清理除自身外的所用进程),清理主要调用killbackgroupprocess方法。


   部分功能展示



 5、手机杀毒

   功能描述:扫描手机中的所用应用,若发现病毒跳转到病毒处理页面供用户选择是否清楚。


   技术实现:通过系统的包管理者PackageManager获取应用包名,对包名进行md5处理,与数据库中的md5病毒名md5对比(手机中的应用是以apk形式存在的)。若为病毒跳转到应用卸载界面供用户清理。


   部分功能展示:  



 6、电话归属地查询

   功能描述:(1)、电话归属地查询,(2)、来去电归属地显示


   技术实现:(1)电话归属地查询:和数据库相关操作,小项目用的比较多封装啥的都差不多;(2)来电归属地显示:显示:自定义WindowManager显示自定义view,来电监听:定义一个来电监听service,监听电话状态获取电话的号码,调用数据库查询这个号码的归属地,并将它显示在view上;归属地风格:(很简单,几个背景);归属地位置设置(通过ontouchevent监听触点位置,获取移动点坐标,并重绘(onmeasure onlayout ondraw(就这三个过程)))不过需要注意点是,在activity中重绘和windowmanager中还是有区别的,详细的对比代码这块有比较详细的注释见(AddressService以及DragViewActivity


         部分功能展示




   7、程序锁

         功能描述对系统所有应用供用户选择加锁并对被加锁程序密码保护


         技术实现1)已加锁与应用和胃加锁应用展示:通过两个fragmengt,用户点击加锁或未加锁fragment上的listviewde item对应用包名增加和删除操作。(2)对加锁程序密码保护:在服务中实现一个看门狗程序,一直监视应用任务栈中的第一个应用,若第一个应用存在在被保护应用数据库中跳转到密码输入界面。


   目前存在问题目前这边跳转界面还未能实现,问题暂时可能的定位是权限问题需要进一步排查吧。


   基本功能展示就到这,还有许多bug,这段时间也是比较忙,bug进展比较慢。而在BaseActivity中做的工作很简单:监视手势滑动,代码也很简单直接看代码:

public abstract class BaseActivity extends Activity{
    private GestureDetector gestureDetector;
    public SharedPreferences mPrefs;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPrefs = getSharedPreferences("config",MODE_PRIVATE);

        gestureDetector = new GestureDetector(this,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if(e1.getRawX() - e2.getRawX() > 200){
                    showNextPage();
                    return true;
                }else if(e2.getRawX() - e1.getRawX() > 200)
                {
                    showPreviousPage();
                    return true;
                }
                return super.onFling(e1, e2, velocityX, velocityY);
            }
        });
    }

    protected abstract void showPreviousPage();

    protected abstract void showNextPage();

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetector.onTouchEvent(event);// 委托手势识别器处理触摸事件
        return super.onTouchEvent(event);
    }
}

  splash界面做初始化操作:当用户第一次下载时,将外部数据库拷贝到用户手机sd卡目录下,判断版本更新,判断是否创建桌面快捷方式,以及启动停止service等,还是直接看代码吧:

public class SpashActivity extends AppCompatActivity {
    protected static final int CODE_UPDATE_DIALOG = 0;
    protected static final int CODE_URL_ERROR = 1;
    protected static final int CODE_NET_ERROR = 2;
    protected static final int CODE_JSON_ERROR = 3;
    protected static final int CODE_ENTER_HOME = 4;// 进入主页面

    private TextView tvVersion;
    private RelativeLayout rlRoot;

    // 服务器返回的信息
    private String mVersionName;// 版本名
    private int mVersionCode;// 版本号
    private String mDesc;// 版本描述
    private String mDownloadUrl;// 下载地址

    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case CODE_UPDATE_DIALOG:
                    showUpdateDailog();
                    break;
                case CODE_URL_ERROR:
                    Toast.makeText(SpashActivity.this, "url错误", Toast.LENGTH_SHORT)
                            .show();
                    enterHome();
                    break;
                case CODE_NET_ERROR:
                    Toast.makeText(SpashActivity.this, "网络错误", Toast.LENGTH_SHORT)
                            .show();
                    enterHome();
                    break;
                case CODE_JSON_ERROR:
                    Toast.makeText(SpashActivity.this, "数据解析错误",
                            Toast.LENGTH_SHORT).show();
                    enterHome();
                    break;
                case CODE_ENTER_HOME:
                    enterHome();
                    break;

                default:
                    break;
            }
        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spash);

        copyDB("address.db");// 拷贝归属地查询数据库

        //拷贝资产目录下的病毒数据库文件
        copyDB("antivirus.db");

        startService(new Intent(this, CallSafeService.class));

        //Intent watchDogIntent = new Intent(this, WatchDogService.class);
        //startService(watchDogIntent);

        initView();
        opEvents();
        checkVersion();
    }

    private void opEvents() {
        tvVersion.setText("版本名:" + getVersionName());

        AlphaAnimation alphaAnimation = new AlphaAnimation(0.3f,1f);
        alphaAnimation.setDuration(2000);
        rlRoot.startAnimation(alphaAnimation);
    }

    private String getVersionName() {
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(
                                            getPackageName(),0);
            String  versionName = packageInfo.versionName;
            return versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取本地app的版本号
     *
     * @return
     */
    private int getVersionCode() {
        PackageManager packageManager = getPackageManager();
        try {
            PackageInfo packageInfo = packageManager.getPackageInfo(
                    getPackageName(), 0);// 获取包的信息

            int versionCode = packageInfo.versionCode;
            return versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            // 没有找到包名的时候会走此异常
            e.printStackTrace();
        }

        return -1;
    }

    private void initView() {
        tvVersion = (TextView) findViewById(R.id.tv_version);
        rlRoot = (RelativeLayout) findViewById(R.id.rl_root);
    }

    private void checkVersion() {
        final long startTime = System.currentTimeMillis();
        // 启动子线程异步加载数据
        new Thread() {

            @Override
            public void run() {
                Message msg = Message.obtain();
                HttpURLConnection conn = null;
                try {
                    // 本机地址用localhost, 但是如果用模拟器加载本机的地址时,可以用ip(10.0.2.2)来替换
                    URL url = new URL("http://10.0.2.2:8080/update.json");
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");// 设置请求方法
                    conn.setConnectTimeout(5000);// 设置连接超时
                    conn.setReadTimeout(5000);// 设置响应超时, 连接上了,但服务器迟迟不给响应
                    conn.connect();// 连接服务器

                    int responseCode = conn.getResponseCode();// 获取响应码
                    if (responseCode == 200) {
                        InputStream inputStream = conn.getInputStream();
                        String result = StreamUtils.readFromStream(inputStream);
                        // System.out.println("网络返回:" + result);

                        // 解析json
                        JSONObject jo = new JSONObject(result);
                        mVersionName = jo.getString("versionName");
                        mVersionCode = jo.getInt("versionCode");
                        mDesc = jo.getString("description");
                        mDownloadUrl = jo.getString("downloadUrl");
                        // System.out.println("版本描述:" + mDesc);

                        if (mVersionCode > getVersionCode()) {// 判断是否有更新
                            // 服务器的VersionCode大于本地的VersionCode
                            // 说明有更新, 弹出升级对话框
                            msg.what = CODE_UPDATE_DIALOG;
                        } else {
                            // 没有版本更新
                            msg.what = CODE_ENTER_HOME;
                        }
                    }
                } catch (MalformedURLException e) {
                    // url错误的异常
                    msg.what = CODE_URL_ERROR;
                    e.printStackTrace();
                } catch (IOException e) {
                    // 网络错误异常
                    msg.what = CODE_NET_ERROR;
                    e.printStackTrace();
                } catch (JSONException e) {
                    // json解析失败
                    msg.what = CODE_JSON_ERROR;
                    e.printStackTrace();
                } finally {
                    long endTime = System.currentTimeMillis();
                    long timeUsed = endTime - startTime;// 访问网络花费的时间
                    if (timeUsed < 2000) {
                        // 强制休眠一段时间,保证闪屏页展示2秒钟
                        try {
                            Thread.sleep(2000 - timeUsed);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    mHandler.sendMessage(msg);
                    if (conn != null) {
                        conn.disconnect();// 关闭网络连接
                    }
                }
            }
        }.start();
    }

    /**
     * 升级对话框
     */
    protected void showUpdateDailog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("最新版本:" + mVersionName);
        builder.setMessage(mDesc);
        // builder.setCancelable(false);//不让用户取消对话框, 用户体验太差,尽量不要用
        builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                System.out.println("立即更新");
                download();
            }
        });

        builder.setNegativeButton("以后再说", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                enterHome();
            }
        });

        // 设置取消的监听, 用户点击返回键时会触发
        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {

            @Override
            public void onCancel(DialogInterface dialog) {
                enterHome();
            }
        });

        builder.show();
    }

    /**
     * 下载apk文件
     */
    protected void download() {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {

            //tvProgress.setVisibility(View.VISIBLE);// 显示进度

            String target = Environment.getExternalStorageDirectory()
                    + "/update.apk";
            // XUtils
            HttpUtils utils = new HttpUtils();
            utils.download(mDownloadUrl, target, new RequestCallBack<File>() {

                // 下载文件的进度
                @Override
                public void onLoading(long total, long current,
                                      boolean isUploading) {
                    super.onLoading(total, current, isUploading);
                   // System.out.println("下载进度:" + current + "/" + total);
                   // tvProgress.setText("下载进度:" + current * 100 / total + "%");
                }

                // 下载成功
                @Override
                public void onSuccess(ResponseInfo<File> arg0) {
                    System.out.println("下载成功");
                    // 跳转到系统下载页面
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.addCategory(Intent.CATEGORY_DEFAULT);
                    intent.setDataAndType(Uri.fromFile(arg0.result),
                            "application/vnd.android.package-archive");
                    // startActivity(intent);
                    startActivityForResult(intent, 0);// 如果用户取消安装的话,
                    // 会返回结果,回调方法onActivityResult
                }

                // 下载失败
                @Override
                public void onFailure(HttpException arg0, String arg1) {
                    Toast.makeText(SpashActivity.this, "下载失败!",
                            Toast.LENGTH_SHORT).show();
                }
            });
        } else {
            Toast.makeText(SpashActivity.this, "没有找到sdcard!",
                    Toast.LENGTH_SHORT).show();
        }
    }

    // 如果用户取消安装的话,回调此方法
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        enterHome();
        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * 进入主页面
     */
    private void enterHome() {
        Intent intent = new Intent(this, HomeActivity.class);
        startActivity(intent);
        finish();
    }

    /**
     * 拷贝数据库
     *
     * @param dbName
     */
    private void copyDB(String dbName) {
        File filesDir = getFilesDir();
        System.out.println("路径:" + filesDir.getAbsolutePath());
        File destFile = new File(getFilesDir(), dbName);// 要拷贝的目标地址

       if (destFile.exists()) {
            System.out.println("数据库" + dbName + "已存在!");
            return;
        }

        FileOutputStream out = null;
        InputStream in = null;

        try {
            //in = getAssets().open(dbName);
            in = getClassLoader().getResourceAsStream("assets/" + dbName);
            int length = in.available();
            //System.out.println("databasesize" + length);
            out = new FileOutputStream(destFile);

            int len = 0;
            byte[] buffer = new byte[1024];

            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 


   源码下载地址:仿360手机卫士简易实现版

    github免积分源码下载地址:github源码

你可能感兴趣的:(【android】仿360手机卫士的简易设计思路及源码)