在吴昊系列的吴昊品工程项目的Round 3中,我介绍过一款以前的“异世界”团队(我和几个人做爱立信的比赛时候完成)的软件,叫Mobile Safe,其实,这样的软件已经有不少,而且其中的很多都是非商业的。主要是因为目前的手机防盗已经出现一些直接植入到手机(硬件)内的嵌入式产品了,这 样即使你的手机丢失,别人将你的SIM卡更换或者操作系统重刷之后,你仍然可以找到你原来的手机。不过,这里,我想介绍一款叫做LostPrevent的 软件,它是附着在操作系统上的,别人刷机你木有办法,但是,作为历史性的手机防盗软件,至少可以纳为经典的行列吧!
LostPrevent的功能:
手机防盗远程控制软件,包括两项功能,
一是:手机防盗,开启防盗功能后,如果手机被盗,并更换了其他sim卡,手机就会自动发送一条短信到您预先设定好的手机上,短信中包含更换的新的sim卡的信息,便于您根据sim的信息找回您丢失的手机。
二是:短信远程控制功能,通 过短信发送相应的命令便可以控制您的手机,例如:如果您的手机设为了静音,但这个时候您又不知道手机放在哪了,那么您便可以向 手机发送相应的短信命令,手机收到命令后便会自动将声音调到最大,然后播放音乐,这样您就可以轻松找到您的手机了,再比如,如果您的手机遗忘在家中,但是 这个时候,您又怕漏接了电话,那么您就可以像手机发送相应的命令,让您的手机呼叫转移到另外一个号码,让您轻松应对这种危难情况。
工程目录:
布局文件夹中的两个XML文件以及src包里面的三个JAVA文件,bin目录里面有APK安装文件,values装载键值,AndroidManifest里面装的是注册信息,gen装载R文件。
Layout布局的实现:
(1)登录界面:
//声明版本号和编码格式(utf-8)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" //布满整个区域的竖直布局
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
> //创建两个可以编辑的文本框(EditText),hint表示暗显示的文字,本人输入之后会撤销。
<EditText
android:id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入用户名"
></EditText>
<EditText
android:id="@+id/password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="true"
android:hint="请输入密码"
></EditText> //下面的这一条,插入一个子布局,在一行上放置权重为1:1的两个button控件,控件中显示的文字分别为"OK"和"CANCLE"。
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK"
android:layout_weight="1"
></Button>
<Button
android:id="@+id/cancle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CANCLE"
android:layout_weight="1"
></Button>
</LinearLayout>
</LinearLayout> (2)主界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText //这里第一行为为EditText注册的控件名safenumber
android:id="@+id/safenumber"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入一个安全有效的手机号码"
></EditText>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始防盗"
android:layout_weight="1"
></Button>
<Button
android:id="@+id/modify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="修改密码"
android:layout_weight="1"
></Button>
</LinearLayout>
</LinearLayout>
这里说下,关于android的程式设计,其实就是XML(布局)+JAVA(实现内部逻辑),所以有的人说android编程其实是Web meets phone,就是这个道理,其中有许多与网页编码类似的特性。说完了布局之后,可以看下src文件夹中的源代码:
//主Activity(ActivityMain):
package com.zhengping.lp;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.location.LocationManager;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class ActivityMain extends Activity {
//定义各种控件的控件名
AlertDialog setPasswordDialog;
AlertDialog showPasswordDialog; //用SharedPreferences来实现持久存储
SharedPreferences sp;
EditText et_safenumber;
Button btn_start;
Button btn_modify;
boolean isFirst;
boolean isStart;
TelephonyManager tm;
@Override //重写onCreate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //得到电话服务
tm = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
sp = this.getSharedPreferences("data", MODE_WORLD_READABLE); //取得数据之后,判断是不是第一次使用,如果是的话,弹出“设置密码”的对话框,而如果不是的话,则弹出“输入密码”的对话框
isFirst = sp.getBoolean("first", true);
if(isFirst) {
showSetPasswordDialog();
return;
} else {
showInputPasswordDialog();
}
}
private void showInputPasswordDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.showpassworddialog, null); //找到在XML中的控件id
final EditText et_username = (EditText) view.findViewById(R.id.username);
final EditText et_password = (EditText) view.findViewById(R.id.password);
Button btn_ok = (Button) view.findViewById(R.id.ok);
Button btn_cancle = (Button) view.findViewById(R.id.cancle);
//设置OK按钮的监听
btn_ok.setOnClickListener(new OnClickListener() {
public void onClick(View v) { //提取用户输入的字符串的内容,并将其存储在username和password中
String username = et_username.getText().toString();
String password = et_password.getText().toString();
//判断是有用户名和密码有空的情况,有的话显示一个长的Toast
if(username.trim().equals("") || password.trim().equals("")) {
Toast.makeText(ActivityMain.this, "用户名和密码不能为空", Toast.LENGTH_LONG).show();
return;
} //提取出持久存储的用户名和密码的字符串
String savedUsername = sp.getString("username", "");
String savedPassword = sp.getString("password", "");
if(username.trim().equals(savedUsername) && password.trim().equals(savedPassword)) { //比对无误,初始化,并将密码对话框消去
init();
showPasswordDialog.dismiss();
} else { //显示一个长时的Toast
Toast.makeText(ActivityMain.this, "用户名或者密码错误", Toast.LENGTH_LONG).show();
return;
}
}});
//同理,对cancle按钮也设置监听
btn_cancle.setOnClickListener(new OnClickListener() {
public void onClick(View v) { //利用finish()函数退出
finish();
}});
//设置对话框的标题,创建并显示对话框
builder.setTitle("输入密码");
builder.setView(view);
showPasswordDialog = builder.create();
showPasswordDialog.show();
}
private void init() {
setContentView(R.layout.main); //同样地,先声明
et_safenumber = (EditText) this.findViewById(R.id.safenumber);
btn_start = (Button) this.findViewById(R.id.start);
btn_modify = (Button) this.findViewById(R.id.modify);
String savedNumber = sp.getString("safenumber", ""); //显示出原来保存的数据的安全电话号码safenumber
et_safenumber.setText(savedNumber);
isStart = sp.getBoolean("start", false);
if(isStart) { //如果“开始防盗”已经点开了,将et_safenumber的那个EditText控件和modify(修改密码)设置为不可见的状态,并将原来显示"开始防盗"的按钮的名字改变为"停止防盗"
et_safenumber.setEnabled(false);
btn_modify.setEnabled(false);
btn_start.setText("停止防盗");
}
btn_start.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
if(isStart) { //退出防盗,返回来原来的状态
Editor editor = sp.edit();
editor.putBoolean("start", false);
editor.commit();
et_safenumber.setEnabled(true);
btn_modify.setEnabled(true);
btn_start.setText("开始防盗");
isStart = false;
} else {
String safeNumber = et_safenumber.getText().toString(); //提示手机安全号码不能为空
if(safeNumber.trim().equals("")) {
Toast.makeText(ActivityMain.this, "安全号码不能为空,请重新设置", Toast.LENGTH_LONG).show();
return;
} else {
String phoneNumber = tm.getLine1Number();
//IMSI
String subScribeerId = tm.getSubscriberId();
//利用putString方法读入safenumber
Editor editor = sp.edit();
editor.putString("safenumber", safeNumber);
isStart = true;
editor.putBoolean("start",isStart);
editor.putString("subscriberid", subScribeerId);
editor.commit();
btn_modify.setEnabled(false);
et_safenumber.setEnabled(false);
btn_start.setText("停止防盗");
}
}
}});
//修改密码按钮的监听
btn_modify.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
showSetPasswordDialog();
}});
}
//这个与showInputPasswordDialog函数不同的是,由于是修改密码,所以,需要重新调用putString方法将用户名和密码的数据进行存储
private void showSetPasswordDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.showpassworddialog, null);
final EditText et_username = (EditText) view.findViewById(R.id.username);
final EditText et_password = (EditText) view.findViewById(R.id.password);
Button btn_ok = (Button) view.findViewById(R.id.ok);
Button btn_cancle = (Button) view.findViewById(R.id.cancle);
btn_ok.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String username = et_username.getText().toString();
String password = et_password.getText().toString();
if(username.trim().equals("") || password.trim().equals("")) {
Toast.makeText(ActivityMain.this, "用户名和密码不能为空", Toast.LENGTH_LONG).show();
return;
}
Editor editor = sp.edit();
editor.putString("username", username);
editor.putString("password", password);
editor.putBoolean("first", false);
editor.commit();
setPasswordDialog.dismiss();
init();
}});
btn_cancle.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if(isFirst) {
finish();
} else {
setPasswordDialog.dismiss();
}
}});
builder.setTitle("设置密码");
builder.setView(view);
setPasswordDialog = builder.create();
setPasswordDialog.show();
}
}
//BootCompleteReceiver.java:
package com.zhengping.lp;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
//android有四大组建,这个控件继承于BroadcastReceiver,其用于接收广播控件
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_WORLD_WRITEABLE);
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
boolean isStart = sp.getBoolean("start", false);
if(!isStart) {
return;
} else {
String saveSubscriberId = sp.getString("subscriberid", "");
String safenumber = sp.getString("safenumber", "");
//IMSI
String subscriberId = tm.getSubscriberId();
System.out.println(subscriberId); //判断SIM卡是否已经被更换
if(subscriberId.trim().equals(saveSubscriberId)) {
return;
} else {
//SmsManager,开启短信服务
SmsManager manager = SmsManager.getDefault();
List<String> message = manager.divideMessage("手机IMSI码为:" + saveSubscriberId + " 的手机,SIM已被更换,更换的IMSI码为:" + subscriberId);
for(String msg : message) { //向号码为safenumber的手机发送短信信息
manager.sendTextMessage(safenumber, null, msg, null, null);
}
}
}
}
}
SMSReceivedBroadcastReceiver:短信接收广播
package com.zhengping.lp;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
public class SMSReceivedBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_WORLD_WRITEABLE);
boolean isStart = sp.getBoolean("start", false);
String savePhoneNumber = sp.getString("safenumber", "");
if(isStart) {
Object[] object = (Object[]) intent.getSerializableExtra("pdus");
byte[][] pdus = new byte[object.length][];
for(int i=0;i<pdus.length;i++) {
pdus[i] = (byte[]) object[i];
}
SmsMessage[] msgs = new SmsMessage[object.length];
for(int i=0;i<pdus.length;i++) {
msgs[i] = SmsMessage.createFromPdu(pdus[i]);
}
for(int i=0;i<msgs.length;i++) {
String oriAddress = msgs[i].getDisplayOriginatingAddress();
if(oriAddress.trim().equals(savePhoneNumber)) {
String body = msgs[i].getDisplayMessageBody();
if(body.contains("ilovedog")) {
SmsManager manager = SmsManager.getDefault();
List<String> message = manager.divideMessage("your phone is mine");
for(String msg : message) {
manager.sendTextMessage(savePhoneNumber, null, msg, null, null);
}
}
}
}
}
}
}