mobile_numen_6

1,程序锁

2 屏蔽后退键.


1,程序锁

就是用户把某个程序设置密码,当启动的时候需要输入密码或者其他凭证.

1>首先我们要把系统中所有的程序都列出来.

2>然后创建数据库,把需要锁定的程序的package添加到数据库.

3>再通过contentProvider把数据的增删改查暴露给外部.

4>新建一个程序,主要是提供服务的,不停的监视打开的程序,这是一个远程的服务,能够被上面的程序启动它的服务.

5>通过内容观察者获取内容提供者提供的内容.如获取所有数据的方法.

6>然后注册内容观察者,如果数据库发生改变,重新获取要锁定程序的集合,保证集合是最新的.前提是设置了通知(notifyChange(uri,null)).

7>获取任务栈正在运行的程序,判断是否集合里面,如果在则弹出密码输入框.

这是实现该功能的主要逻辑.

那么我们一步步来实现.

获取所有的系统应用程序,在前面已经讲过了.

public List<AppInfo> getAppInfos() {
		List<AppInfo> applicationInfos = new ArrayList<AppInfo>();
		
		appInfos = pm
				.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
		for (ApplicationInfo appinfo : appInfos) {
			// Log.i(TAG, "packname ="+ appinfo.packageName);
			// Log.i(TAG, "程序的名字 ="+ appinfo.loadLabel(pm));
			// Log.i(TAG, "---------------分隔符---------");
			// 获取程序图标appinfo.loadIcon(pm)
			AppInfo info = new AppInfo();
			// 设置程序的名字
			info.setAppname(appinfo.loadLabel(pm).toString());
			//设置程序的图标
			info.setIcon(appinfo.loadIcon(pm));
			//设置程序的包名
			info.setPackname(appinfo.packageName);
			// 获取当前程序既有启动能力的activity的名字
			try {
				PackageInfo packinfo = pm.getPackageInfo(appinfo.packageName,
						PackageManager.GET_ACTIVITIES);
				ActivityInfo[] activityinfos = packinfo.activities;
				if (activityinfos!=null && activityinfos.length > 0) {
					ActivityInfo activtiyInfo = activityinfos[0];
					String name = activtiyInfo.name;
					info.setLuncheractivity(name);
				}
			} catch (NameNotFoundException e) {
				e.printStackTrace();
			}
			if(filterApp(appinfo)){
				info.setThirdParty(true);
			}else {
				info.setThirdParty(false);
			}

			applicationInfos.add(info);
			info = null;
		}
		return applicationInfos;
	}

② 把需要程序锁的程序放进数据库里面.

创建数据库:

public class LockAppDBOpenHelper extends SQLiteOpenHelper {

	public LockAppDBOpenHelper(Context context) {
		super(context, "lock.db", null, 1);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("create table lockapp (_id integer primary key autoincrement, packname varchar(20))");
	}

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

	}
}

创建dao类:

public class LockAppDao {
	Context context;
	LockAppDBOpenHelper dbOpenHelper;
	private static final Uri uri = Uri
			.parse("content://cn.itcast.db.lockappprovider/lockapps");

	public LockAppDao(Context context) {
		this.context = context;
		dbOpenHelper = new LockAppDBOpenHelper(context);
	}

	/**
	 * save 保存 把锁定程序的包名保存到数据库
	 * 
	 * @param packname
	 *            包名
	 */
	public void save(String packname) {
		if (find(packname))
			return;
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		if (db.isOpen()) {
			db.execSQL("insert into lockapp (packname) values(?)",
					new Object[] { packname });
			
			context.getContentResolver().notifyChange(uri, null);
			db.close();
		}
	}

	/**
	 * delete 删除 把锁定程序包名从数据库删除
	 */
	public void delete(String packname) {
		SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
		if (db.isOpen()) {
			db.execSQL("delete from lockapp where packname=?",
					new Object[] { packname });
			
			context.getContentResolver().notifyChange(uri, null);

			db.close();
		}
	}
........由于篇幅的原因,这里只写显示2个方法,其他的省略.

}

  从上面可以看出我们还需要contentProvider 如③

添加到数据库的界面就不写了

public class LockAppProvider extends ContentProvider {
	// 创建一个uri数据的匹配器
	private static final UriMatcher matcher = new UriMatcher(
			UriMatcher.NO_MATCH);
	private static final int ALLAPP = 1;
	LockAppDao dao;
	static {
		// 在匹配器里面增加了一条规则 , 如果 content://cn.itcast.db.lockappprovider/lockapps
		// matcher的返回值为 1
		matcher.addURI("cn.itcast.db.lockappprovider", "lockapps", ALLAPP);
	}
	
	
	@Override
	public boolean onCreate() {
		dao = new LockAppDao(getContext());
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		switch (matcher.match(uri)) {
		case ALLAPP:
			return dao.findallcursor();
		default:
			throw new IllegalArgumentException("uri 不能被识别 ");
		}
	}

	@Override
	public String getType(Uri uri) {
		switch (matcher.match(uri)) {
		case ALLAPP:
			return "vnd.android.cursor.dir/allapps";
		default:
			// 如果 uri不符合
			throw new IllegalArgumentException("uri 不能被识别 ");
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		return 0;
	}
}

在写dao的时候可以发现save和delete方法都有

context.getContentResolver().notifyChange(uri, null);这个作用就是如果uri指定的数据发生了改变,就通知通过
registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)注册的内容观察者.

④新建一个程序叫做remoteLockApp

while (true) {
					// 1.获取当前前台的activity对应的包名
					List<RunningTaskInfo> runninginfos = am.getRunningTasks(1);
					RunningTaskInfo info = runninginfos.get(0);
					String packname = info.topActivity.getPackageName();
					// Log.i(TAG,"当前前台activity的包名是 "+ packname);
					//用户如果到home界面,那么把集合里的程序都设为不能启动
					//要不然用户输入密码成功后,以后进入程序就不需要输入密码,这不是我们想要的.
					if ("com.android.launcher".equals(packname)) {
                            //把数据库里记录的所有的程序都改成不可启动
						Set<Entry<String, Boolean>> entryset = map.entrySet();
						for (Entry<String, Boolean> entry : entryset) {
							String key = entry.getKey();
							map.put(key, false);
						}
					}

					// 3.遍历这个集合 比对如果当前的activity对应的包名在集合中 弹出界面让用户输入密码
					for (String dbpackname : packnames) {//其实不需要遍历集合,可以使用jdk提供的api list.contains(object)效率要高些
						if (packname.equals(dbpackname)) {//判断当前的报名是否在集合里面
							// 判断当前的包名 是否可以被启动
							if (map.get(packname)) {
								// 可以被启动
								continue;
							} else {
								// 开启密码输入界面
								intent.putExtra("packname", packname);
								startActivity(intent);
							}
							break;
						}
					}
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

  

/**
	 * 获取所有被锁定的程序的包名的集合
	 */
	private void getLockAppPacknames() {
		packnames.clear();
		Cursor cursor = getContentResolver().query(uri, null, null, null, null);
		while (cursor.moveToNext()) {
			// 循环初始化map集合
			String packname = cursor.getString(cursor
					.getColumnIndex("packname"));
			packnames.add(packname);
			// 初始化程序 , 在map集合里面 放置信息 默认包名不可以被启动
			map.put(packname, false);
		}
		cursor.close();
	}

  

this.getContentResolver().registerContentObserver(uri, true,
				new LockDBObserver(new Handler()));

 

	private class LockDBObserver extends ContentObserver {
		public LockDBObserver(Handler handler) {
			super(handler);
		}

		// 当数据库内容发生改变的时候调用的方法
		@Override
		public void onChange(boolean selfChange) {

			Log.i(TAG, "数据库内容发生改变 ");
			// 获取要锁定程序的集合.
			getLockAppPacknames();
			super.onChange(selfChange);
		}

	}

 

for (String dbpackname : packnames) {
						if (packname.equals(dbpackname)) {
							// 判断当前的包名 是否可以被启动
							if (map.get(packname)) {
								// 可以被启动
								continue;
							} else {
								// 开启密码输入界面
								intent.putExtra("packname", packname);
								startActivity(intent);
							}
							break;
						}
					}

 上面的代码都是运行正确的代码,在这之前遇到了如下问题 :

1>当我们输入密码正确,程序闪了一下又要我们输入密码.
2>上面问题解决后,当我们输入密码正确,顺利进入程序,最后退出进入home界面,在进入上次进入的程序发现不需要输入密码.
这不是我们需要的,我们需要的是每一次进入都需要输入密码.

对于第一个问题,因为那个while(true)循环,尽管我们输入了正确的密码,当他检测到当前的package是数据库里面的,那么他又会重新弹出输入密码界面.

所以我们多加了一个map,把数据库的数据放进了集合并作了标记,默认是false如果输入密码正确则把他置为true(通过调用服务里面的方法把他设置为true)那么就不会进入密码输入界面了

// 判断当前的包名 是否可以被启动
if (map.get(packname)) {
// 可以被启动
continue;
}

对于第二个问题,代码里已经说的很清楚了,所以就做了一下判断,当用户进入了home界面,那么把所有的数据库里包含的程序报名都置为false.那么下次进来的时候就需要设置密码了.

					if ("com.android.launcher".equals(packname)) {
						Set<Entry<String, Boolean>> entryset = map.entrySet();
						for (Entry<String, Boolean> entry : entryset) {
							String key = entry.getKey();
							map.put(key, false);
						}
					}

  

总结:contentProvider的两个作用

把数据暴露给外部程序使用.

如果设置了notifyChange(...),就会通知通过registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)注册的内容观察者.

 

 

2 屏蔽后退键.

对于上面的程序来说,如果弹出密码输入界面,用户通过后退键使得密码输入界面(实际上是一个Activity)消失.这样不符合我们的要求.

	/**
	 * 屏蔽掉后退按键
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
			return false;
		}
		return super.onKeyDown(keyCode, event);
	}

  

posted @ 2012-02-11 23:13  johnny901114  阅读(190)  评论(0编辑  收藏  举报