• 如我们所知,现在有很多牛逼的安全软件都带有程序锁的功能。当然,名字可能不一样,有的叫隐私保护,有的叫软件锁。等等。但是这种名字其实都是表达一种意思,就是可以给你手机里面的app加以保护。只有你给你的App设置了程序锁,这样每次在你进入这个App的时候就要先输入密码,然后才能进入。
  • 程序锁功能并不是什么高大上的技术,也不是很难的技术点。于是,受到灰驹的启发,我就写了一个简单的,UI比较丑陋的程序锁的小Demo。
  • 国际惯例:先分析一下做一个程序锁功能所涉及到的知识点:
  • Service组建的使用。
  • Broadcast组建的使用(当然,这个不是必须的,在实现这个功能上)。
  • Activity启动模式的了解。
  • ActivityManager的APi使用。
  • PackageManager的API的使用。
  • 数据库SQLiteDatabase的使用。(这个也不是必须的)
  • InputMethodManager的API的使用。(有些手机不主动打开软键盘,就需要使用这个类)
  • OK,介绍就这些了,下面是代码区:
  • 关于UI方面,简单说明一下,里面就一个主Activity,作用就是让用户选择添加需要加锁的应用,进行加锁操作。然后就是一个弹出让用户给加锁的应用输入密码的Activity。在用户点击要打开已经加锁的应用时候弹出。
  • 首先是xml代码:
  • MainActivity的布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:gravity="center_horizontal"
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="8dp"
        android:background="@android:color/darker_gray"
        android:clickable="true"
        android:padding="8dp"
        android:text="未加锁"
        android:textColor="@android:color/white"
        android:textSize="16sp" />

    <ListView
        android:id="@+id/lockListView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvTitle" >
    </ListView>

</RelativeLayout>
* 输入密码界面的布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:padding="10dp"
        android:text="加锁了" />

    <EditText
        android:id="@+id/et_pwd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/textView1" />

    <Button
        android:onClick="open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="99dp"
        android:text="芝麻开门" />

</RelativeLayout>
  • xml布局就没有了。
  • 需要注意一下,程序锁功能里面需要添加的权限:
<uses-permission android:name="android.permission.GET_TASKS" />
  • 然后是Java代码:
  • 首先是程序的MainActivity
package com.duck.husband;

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnItemClickListener {

    private ListView listView;
    private Context context;
    private AppInfoDao infoDao;
    private List<String> unLockedDatas;
    private List<String> lockedDatas;
    private List<String> adapterDatas;
    private TextView tvTitle;
    private LockAdapter adapter;
    private WatchDogDao watchDogDao;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, WatchDogService.class);
        startService(intent);
        watchDogDao = new WatchDogDao(context);
        updateData();
        initTitle();
        initListView();
    }

    private void initTitle() {
        tvTitle = (TextView) findViewById(R.id.tvTitle);
        tvTitle.setText("未加锁");
        adapterDatas = unLockedDatas;
        tvTitle.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (tvTitle.getText().toString().equals("未加锁")) {
                    tvTitle.setText("已加锁");
                    // TODO: 去 已加锁界面
                    adapterDatas = lockedDatas;
                } else {
                    tvTitle.setText("未加锁");
                    // TODO:去 未加锁界面
                    adapterDatas = unLockedDatas;
                }
                adapter.notifyDataSetChanged();
            }
        });
    }

    private void updateData() {
        infoDao = new AppInfoDao();
        unLockedDatas = infoDao.getAllApps(context);
        lockedDatas = watchDogDao.queryAllInfos();
        for (String text : lockedDatas) {
            if (unLockedDatas.contains(text))
                unLockedDatas.remove(text);
        }
    }

    private void initListView() {
        setTitle("程序锁功能");
        listView = (ListView) findViewById(R.id.lockListView);
        adapter = new LockAdapter();
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);
    }

    private class LockAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return adapterDatas.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = new TextView(context);
            }
            TextView textView = (TextView) convertView;
            textView.setPadding(8, 8, 8, 8);
            textView.setTextSize(18);
            textView.setText(adapterDatas.get(position));
            return textView;
        }

    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        System.out.println("position: " + position);
        System.out.println("id: " + id);
        vtoast("position: " + position + "\n id: " + id);
        if (tvTitle.getText().toString().equals("未加锁")) {
            String removedPackageName = unLockedDatas.remove(position);
            watchDogDao.insert(removedPackageName);
            lockedDatas.add(removedPackageName);
            adapter.notifyDataSetInvalidated();
        } else {
            String removedPackageName = lockedDatas.remove(position);
            watchDogDao.delete(removedPackageName);
            unLockedDatas.add(removedPackageName);
            adapter.notifyDataSetInvalidated();
        }

    }

    protected void vtoast(String text) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }
}
  • 然后是WatchDogService
package com.duck.husband;

import java.util.List;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.SystemClock;

public class WatchDogService extends IntentService {
    private static final boolean DEBUG = false;
    private static final String COM_DUCK_HUSBAND_UNCHECKED = "com.duck.husband.UNCHECKED";
    private Context context;
    private WatchDogDao watchDogDao;
    private String unCheckedPackageName;
    private UnCheckedReceiver receiver;

    public WatchDogService() {
        super("abcde");
        context = this;
        watchDogDao = new WatchDogDao(context);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        receiver = new UnCheckedReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(COM_DUCK_HUSBAND_UNCHECKED);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(receiver, filter);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        while (true) {
            ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
            List<RunningTaskInfo> runningTasks = am.getRunningTasks(99);
            String packageName = runningTasks.get(0).topActivity
                    .getPackageName();
            // 获取最近打开的App包名
            boolean b = watchDogDao.query(packageName);
            if (b) {
                // 说明是加锁的程序
                if (packageName.equals(unCheckedPackageName)) {
                } else {
                    Intent intent2 = new Intent(context, LockActivity.class);
                    intent2.putExtra("packageName", packageName);// TODO:这一行不加,就没有办法去临时取消保护了!!!
                    intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent2);
                }
            } else {
            }
            if (DEBUG) {
                System.out.println("packageName 0:" + packageName);
            }
            SystemClock.sleep(300);
        }
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (receiver != null) {
            unregisterReceiver(receiver);
            receiver = null;
        }
    }

    class UnCheckedReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
                if (intent.getAction().equals(COM_DUCK_HUSBAND_UNCHECKED)) {
                    unCheckedPackageName = intent.getStringExtra("packageName");
                    System.out.println("unCheckedPackageName: "
                            + unCheckedPackageName);
                } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                    unCheckedPackageName = null;
                }
            }
        }

    }
}
  • 然后是将需要包含的App包名存放到数据库的代码
package com.duck.husband;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class WatchDogOpenHelper extends SQLiteOpenHelper {

    public static final String TABLENAME = "appLocktb";
    public WatchDogOpenHelper(Context context, String name, int version) {
        super(context, name, null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table if not exists appLocktb(_id integer primary key, packageName text not null)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

}

以及

package com.duck.husband;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class WatchDogDao {

    public static final String TABLENAME = WatchDogOpenHelper.TABLENAME;
    public static final String PACKAGENAME = "packageName";
    private WatchDogOpenHelper openHelper;

    public WatchDogDao(Context context) {
        openHelper = new WatchDogOpenHelper(context, "watchDog.db", 1);
        // "create table if not exists appLocktb(_id integer primary key, packageName text not null)");
    }

    /**
     * 插入一条数据,如果数据已经存在,就不插入
     * 
     * @param packageName
     * @return 插入成功.失败
     */
    public boolean insert(String packageName) {
        boolean insert = false;
        SQLiteDatabase db = openHelper.getWritableDatabase();

        Cursor cursor = db.rawQuery(
                "select * from appLocktb where packageName=?",
                new String[] { packageName });

        if (cursor != null) {
            insert = cursor.moveToNext();
            cursor.close();
        }
        ContentValues values = new ContentValues();
        values.clear();
        if (insert) {
            return insert;
        } else {
            values.put(PACKAGENAME, packageName);
            long data = db.insert(TABLENAME, null, values);
            db.close();
            if (data != -1) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * 删除一条数据,如果数据不存在,就不删除
     * 
     * @param packageName
     * @return 删除成功.失败
     */
    public boolean delete(String packageName) {
        boolean delete = false;
        SQLiteDatabase db = openHelper.getWritableDatabase();

        Cursor cursor = db.rawQuery(
                "select * from appLocktb where packageName=?",
                new String[] { packageName });

        if (cursor != null) {
            delete = cursor.moveToNext();
            cursor.close();
        }
        ContentValues values = new ContentValues();
        values.clear();
        if (delete) {
            // 说明有这条数据,删除
            int data = db.delete(TABLENAME, PACKAGENAME + "=?",
                    new String[] { packageName });
            db.close();
            if(data>0){
                return true;
            }else {
                return false;
            }
        } else {
            // 说明没有有这条数据,删除
            return false;
        }
    }
    /**
     * 查询数据库有没有对应的数据
     * @param packageName
     */
    public boolean  query(String packageName){
        SQLiteDatabase db = openHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select * from appLocktb where packageName=?",
                new String[] { packageName });
        if(cursor!=null){
            boolean moveToNext = cursor.moveToNext();
            cursor.close();
            db.close();
            if(moveToNext)
                return true;
            else {
                return false;
            }
        }
        return false;
    }

    public List<String> queryAllInfos(){
        List<String> packageNames = new ArrayList<String>();
        SQLiteDatabase db = openHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select * from appLocktb", null);
        if(cursor!=null){
            while(cursor.moveToNext()){
                String name = cursor.getString(cursor.getColumnIndex(PACKAGENAME));
                packageNames.add(name);
            }
            cursor.close();
        }
        db.close();
        return packageNames;
    }
}
  • 然后是获取手机里面已安装应用的工具类
package com.duck.husband;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

public class AppInfoDao {

    /**
     * 获取手机中所有的app包名集合
     * @param context
     * @return 包名的集合
     */
    public List<String> getAllApps(Context context){
        List<String> packageNames  = new ArrayList<String>();
        PackageManager pm = context.getPackageManager();
        List<PackageInfo> infos = pm.getInstalledPackages(0);
        for (PackageInfo info : infos) {
            String packageName = info.packageName;
            packageNames.add(packageName);
        }
        return packageNames;
    }
}
  • 最后是输入密码的界面:
package com.duck.husband;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

public class LockActivity extends Activity {

    private static final String COM_DUCK_HUSBAND_UNCHECKED = "com.duck.husband.UNCHECKED";
    private EditText etPwd;
    private Context context;
    private String packageName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
        setContentView(R.layout.lock_ui);
        packageName = getIntent().getStringExtra("packageName");
        etPwd = (EditText) findViewById(R.id.et_pwd);
        etPwd.setOnFocusChangeListener(new OnFocusChangeListener() {

            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                if (hasFocus) {
                    // //显示软键盘//
                    imm.showSoftInputFromInputMethod(v.getWindowToken(), 0);
                } else {
                    // 隐藏软键盘
                    //
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                }

            }
        });
    }

    public void open(View view) {
        String text = etPwd.getText().toString().trim();
        if (text.equalsIgnoreCase("123")) {
            Intent intent = new Intent(COM_DUCK_HUSBAND_UNCHECKED);
            intent.putExtra("packageName", packageName);
            sendBroadcast(intent);
            finish();
        } else {
            vtoast("密码不对");
        }
    }

    @Override
    public void onBackPressed() {
        /*
         * <activity android:name="com.android.launcher2.Launcher"
         * android:launchMode="singleTask" android:clearTaskOnLaunch="true"
         * android:stateNotNeeded="true" android:theme="@style/Theme"
         * android:screenOrientation="nosensor"
         * android:windowSoftInputMode="stateUnspecified|adjustPan">
         * <intent-filter> <action android:name="android.intent.action.MAIN" />
         * <category android:name="android.intent.category.HOME" /> <category
         * android:name="android.intent.category.DEFAULT" /> <category
         * android:name="android.intent.category.MONKEY"/> </intent-filter>
         * </activity>
         */
        // 打开桌面
        Intent intent = new Intent();
        intent.setAction("android.intent.action.MAIN");
        intent.addCategory("android.intent.category.HOME");
        startActivity(intent);
        // super.onBackPressed();
    }

    @Override
    protected void onStop() {
        // TODO Auto-generated method stub
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    }

    protected void vtoast(String text) {
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }
}
  • 以上代码运行OK。
  • 简单说明一下:
  • 对于MainActivity里面的数据操作,还是比较生硬的,应该可以有更好的方式。
  • 特别要注意的是广播的使用,我在写这个Demo的时候,差不多浪费了20多分钟就是为了处理广播。其实广播相对来说并不难。但是使用起来还是比较繁琐的。首先,你要搞一个意图,意图里面要包含你的行为(和数据,数据可以没有),然后以发送广播的方式发送这个意图。然后,你要搞一个对应的接收器,去接收对应的意图,和里面的数据(如果没有发数据,那就没有数据收到。只是收到意图了)。但是,如果就到此为止了,那这个广播就白做了。你还要在你需要接收广播的地方,先去注册这个广播.(这个好像也可以直接写在清单文件里面),注册了之后,算是可以使用这个广播了。然后往往还要去反注册这个广播,在不再使用的时候。总之就是比较麻烦吧。如果仅仅是为了数据的传递。个人更倾向于使用回调接口的形式来传递。但是这次为什么使用广播?因为我对广播不熟悉,也希望通过这个Demo,去熟悉一下广播的使用。
  • 这里面的数据库的操作就没有什么说的,因为这真的是一个非常简单是表,里面几乎没有内容。
  • 最后需要稍微注意一下的是,有的手机在你的EditText 里面不主动打开软件盘。你点半天就是不出来软键盘,这样你就没有办法输入了。于是,这时候就需要去主动打开软键盘让用户可以输入。