Android SOS功能模組開發
一、sos需求
1、在Settings列表項中新增一項SOS
sos設定入口介面
2、求救功能描述:
3.設定緊急號碼:此選項使用者可以從電話本中選擇聯絡人新增到1-5個緊急號碼中,在1-5列表中點選可以直接進入到系統電話本中去選擇;也可以自己手動輸入新增。
4、編輯緊急簡訊內容:緊急簡訊內容可以編輯,修改,刪除
5,觸發規則:
(1)求救電話撥號規則:如果使用者只設置一個SOS求救電話,如果對方無人接聽,將連續撥打知道對方接通,電話每撥打一次,傳送一條求救資訊。傳送資訊次數與電話撥打次數一樣。如果使用者不想繼續再撥打,如果使用者自己結束通話(撥打方),迴圈撥打將停止。如果對方結束通話,繼續撥打緊急聯絡人。
(2)如果使用者設定多個SOS求救號碼,如果第一個緊急聯絡人無人接聽,將自動切換到第二個緊急聯絡人、以此類推、每撥號一次緊急聯絡人,同步傳送一條緊急求救資訊,以此類推。求救資訊與求救電話次數一樣。如果使用者自己結束通話(撥打方),迴圈撥打將停止。如果對方結束通話,將立即撥打下一個緊急聯絡人。
(3)傳送給緊急聯絡人的求救資訊。除了使用者自己編輯好的外,還需帶有GPS定位資訊,SOS求救發起後,軟體自動開啟GPS資訊進行搜星定位,同時開啟SIM卡資料業務輔助定位,定位成功後發出GPS經緯度資訊到緊急聯絡人。
(4)如果使用者沒有設定SOS任何資訊,在待機介面短按進入到設定介面,如果使用者設定了SOS求救資訊,在待機介面長按發出SOS求救電話和資訊。
二、開發及流程
1、設定中新增sos設定項
(1)在packages/apps/Settings/src/com/android/settings/Settings.java中新增SosSettingsActivity和SosNumSettingsActivity,其實這兩個類只是名義上索引類,並不實際存在,他們索引的類在後面會介紹,
(2)在packages/apps/Settings/src/com/android/settings/SettingsActivity.java中新增String[] SETTINGS_FOR_RESTRICTED的陣列中新增
和在String[] ENTRY_FRAGMENTS陣列中天新增
在這裡你才看上面兩個activity索引的兩個正在實現的地方;它的索引只要在Settings的AndroidMainfest.xml中去實現即可,如下圖所示
在方法doUpdateTilesList()上去掛載和設定sos設定項的顯示和隱藏
2、接下來就是在要響應SOS按鍵:
(1)frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中interceptKeyBeforeQueueing(KeyEvent event, int policyFlags)方法中去監聽sos按鍵,新增如下程式碼:
(2)、按鍵下去,當然的去呼叫電話去
3,電話去響應請求
(1)在電話中建立一個sos的服務(SosDialService.java),記得在AndroidMainfest.xml中新增元件,去監聽和接收按鍵請求響應;撥打緊急電話和傳送簡訊
SosDialService.java:
package com.android.dialer;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.WindowManager;
import com.android.internal.telephony.Phone;
import com.android.incallui.Call;
import com.android.incallui.CallList;
import android.telephony.TelephonyManager;
import android.telephony.SubscriptionInfo;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
import android.telephony.SubscriptionManager;
import android.provider.Settings;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
public class SosDialService extends Service {
private String TAG = "SosDialService";
private Context mContext;
private static final long INTERVAL = 5 * 1000;
private Timer timer = null;
private String number;
private int numbers;
private static boolean ifEnd = false;
private static boolean isRuning = false;
private static CallList mCallList = null;
private int state = Call.State.IDLE;
private static TelephonyManager mPhone = null;
private static SubscriptionManager mSubscriptionManager = null;
private static TelecomManager mTelecomManager = null;
private PhoneAccountHandle mPhoneAccount = null;
private SosPhoneStateListener mPhoneState = null;
private int times =0;
private static final String[] mNumberKeys = { "sos_number", "sos_number1",
"sos_number2", "sos_number3", "sos_number4" };
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "onBind");
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mContext = getApplicationContext();
Log.d(TAG, "onCreate, mContext = " + mContext);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Log.d(TAG, "onStartCommand, mContext = " + mContext);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
ifEnd = true;
Log.d(TAG, "onDestroy, ifEnd = " + ifEnd);
timer.cancel();
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.d("lxm", "SosDialService.java onStart, intent = " + intent);
numbers = 0;
ifEnd = false;
state = Call.State.IDLE;
if(mCallList == null){
mCallList = CallList.getInstance();
}
if(timer == null){
timer = new Timer();
}
if (mSubscriptionManager == null) {
mSubscriptionManager = SubscriptionManager.from(mContext);
}
if(mTelecomManager == null){
mTelecomManager = TelecomManager.from(mContext);
}
if(mPhone == null) {
mPhone = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
}
if(mPhoneState == null)
mPhoneState = new SosPhoneStateListener();
mPhone.listen(mPhoneState, PhoneStateListener.LISTEN_SERVICE_STATE);
if (intent.getAction().equals("com.pyt.SOSPHONE") && (isRuning == false)) {
if(mPhone.getSimState(0) != TelephonyManager.SIM_STATE_READY && mPhone.getSimState(1) != TelephonyManager.SIM_STATE_READY){
String message = mContext.getString(R.string.noSIM);
AlertDialog dialog = createDialog(mContext.getString(R.string.Warning), message);
dialog.show();
Log.d(TAG, "SIM no ready!");
return;
}
DialerApplication.startLocation();
mPhoneAccount = mTelecomManager.getUserSelectedOutgoingPhoneAccount();
Log.d(TAG, "onReceive.com.pyt.SOSPHONE, schedule, mPhoneAccount = " + mPhoneAccount);
Log.d(TAG,"timer.start task");
timer.schedule(new TimerTask() {
@Override
public void run() {
int Phonestate = mPhone.getCallState();
Call call = mCallList.getActiveCall();
if(call != null) {
Log.d(TAG, "callList.call = " + call);
state = call.getState();
}
isRuning = true;
Log.d(TAG,"ifEnd ="+ifEnd);
if(ifEnd){
mTelecomManager.setUserSelectedOutgoingPhoneAccount(mPhoneAccount);
Log.d(TAG, "com.pyt.SOSPHONE_CANCEL, ifEnd = " + ifEnd + ", PhoneAccount = " + mPhoneAccount);
isRuning = false;
// timer.cancel();
return;
}
times++;
Log.d(TAG, "times = " + times + ", call.State = " + state + ", Phonestate = " + Phonestate + ", ifEnd = " + ifEnd);
if (state == Call.State.IDLE) {
number = Settings.System.getString(
mContext.getContentResolver(), mNumberKeys[numbers]);
while(number == null || number.length() == 0)
{
if (++numbers > 4) {
numbers = 0;
}
number = Settings.System.getString(
mContext.getContentResolver(), mNumberKeys[numbers]);
}
Log.d(TAG, "numbers = " + numbers + ", number = " + number);
if(Phonestate != TelephonyManager.CALL_STATE_IDLE)
return;
if (null != number && number.length() > 0) {
String sim = Settings.System.getString(mContext.getContentResolver(),"sim_sets");
int index = 0;
if("SIM1".equals(sim)){
index = 0;
}else if("SIM2".equals(sim)){
index = 1;
}
List<PhoneAccountHandle> phoneAccountsList = mTelecomManager.getCallCapablePhoneAccounts();
int size = phoneAccountsList.size();
Log.d(TAG, "index = " + index + ", phoneAccountsList.size() = " + size);
if(size > 0)
{
if (index >= size)
index = 0;
mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountsList.get(index));
}else{
ifEnd = true;
}
Log.d(TAG, "index2 = " + index);
Intent dialIntent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:" + number));
dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
mContext.startActivity(dialIntent);
} catch (ActivityNotFoundException e) {
}
Message msg = mHandlerSosMsg.obtainMessage();
msg.obj = number;
android.util.Log.d(TAG,"SosDialService.java message start");
mHandlerSosMsg.sendMessageDelayed(msg, 30000);
}
if (++numbers > 4) {
numbers = 0;
}
}
if (state == Call.State.ACTIVE) {
mTelecomManager.setUserSelectedOutgoingPhoneAccount(mPhoneAccount);
Log.d(TAG, "timer.cancel(),TelephonyManager.State = " + state + ", PhoneAccount = " + mPhoneAccount);
isRuning = false;
times = 0;
ifEnd = true;
// timer.cancel();
return;
}
}
}, 0, INTERVAL);
}
}
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent
) {
String sim = Settings.System.getString(mContext.getContentResolver(),"sim_sets");
android.util.Log.d(TAG,"SosDialService.java sendTextMessage sim = "+sim);
int index = 0;
if("SIM1".equals(sim)){
index = 0;
}else if("SIM2".equals(sim)){
index = 1;
}
int subInfoLength = 0;
SubscriptionInfo subinfo = null;
List<SubscriptionInfo> SubInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
if(SubInfoList != null)
subInfoLength = SubInfoList.size();
Log.d("", "sendTextMessage, text=" + text + ", destinationAddress=" + destinationAddress +", subInfoLength = " +subInfoLength);
if(subInfoLength <= 0)
return;
else if(subInfoLength == 1) {
subinfo = SubInfoList.get(0);
}
else if(subInfoLength == 2){
subinfo = mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(index);
}
if(subinfo != null){
Log.d(TAG, "SmsManager.sendTextMessage, destinationAddress=" + destinationAddress + ",text=" + text + ",subinfo=" + subinfo);
SmsManager manager = SmsManager.getDefault();
ArrayList<String> list = manager.divideMessage(text);
SmsManager.getSmsManagerForSubscriptionId(subinfo.getSubscriptionId()).sendMultipartTextMessage(destinationAddress,
scAddress, list, null, null);
}
}
public Handler mHandlerSosMsg = new Handler(){
public void handleMessage(android.os.Message msg) {
String number = msg.obj.toString();
String content = Settings.System.getString(mContext.getContentResolver(), "sos_content");
android.util.Log.d(TAG,"SosDialService.java mHandlerSosMsg number = "+number+" content = "+content +" Locflag = "+DialerApplication.getLocflag());
if(DialerApplication.getLocflag() < 3) {
Message msg2 = mHandlerSosMsg.obtainMessage();
msg2.obj = number;
mHandlerSosMsg.sendMessageDelayed(msg2, 30000);
Log.d(TAG, "number = " + number + ",locFlag =" + DialerApplication.getLocflag() + ",content =" + content);
}else {
android.util.Log.d(TAG,"SosDialService.java mHandlerSosMsg boolean = "+DialerApplication.getLocationInfo().isEmpty());
if(!DialerApplication.getLocationInfo().isEmpty()){
if(content == null)
content = mContext.getString(R.string.gps_info) + "\n" + DialerApplication.getLocationInfo();
else
content = content + "\n" + mContext.getString(R.string.gps_info) + "\n" + DialerApplication.getLocationInfo();
}
Log.d(TAG, "sos sms send, number = " + number + " ,content =" + content);
if(null == content || content.length() == 0){
content = mContext.getString(R.string.help);
}
try {
Log.d(TAG, "handleMessage ->sendTextMessage, number = " + number + " ,content =" + content);
sendTextMessage(number, null, content, null, null);
} catch (Exception e) {
Log.e(TAG, "error:" + e);
}
}
};
};
private class SosPhoneStateListener extends PhoneStateListener {
public void onServiceStateChanged(ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
int pos = serviceState.getState();
Log.d(TAG, "Phonestate change==" + pos);
if(pos != ServiceState.STATE_IN_SERVICE){
ifEnd = true;
}
}
};
private AlertDialog createDialog(String title, String message) {
final AlertDialog dialog = new AlertDialog.Builder(DialerApplication.getDialerContext())
.setTitle(title)
.setIcon(com.android.internal.R.drawable.ic_dialog_alert)
.setCancelable(false)
.setNegativeButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "invalid sim card ");
}
})
.setMessage(message)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
return dialog;
}
}
(2) 我們傳送的緊急簡訊內容包含了地址資訊,這裡我們用到百度地圖的服務(這裡可以去了解一下如何引入百度地圖的服務:http://lbsyun.baidu.com/apiconsole/key/create),在Dialer/src/com/android/dialer/DialerApplication.java中去獲取到百度地圖服務資訊!
DialerApplication.java:
private void initLocation() {
Log.d("lxm", "DialerApplication.java initLocation ");
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationMode.Hight_Accuracy);//可選,預設高精度,設定定位模式,高精度,低功耗,僅裝置
option.setCoorType("gcj02");//可選,預設gcj02,設定返回的定位結果座標系,
int span=1000;
option.setScanSpan(span);//可選,預設0,即僅定位一次,設定發起定位請求的間隔需要大於等於1000ms才是有效的
option.setIsNeedAddress(true);//可選,設定是否需要地址資訊,預設不需要
option.setOpenGps(true);//可選,預設false,設定是否使用gps
option.setLocationNotify(true);//可選,預設false,設定是否當gps有效時按照1S1次頻率輸出GPS結果
option.setIgnoreKillProcess(true);//可選,預設true,定位SDK內部是一個SERVICE,並放到了獨立程序,設定是否在stop的時候殺死這個程序,預設不殺死
if(mLocationClient ==null){
Log.d("lxm", "DialerApplication.java initLocation DialerApplication->sContext = " + sContext);
mLocationClient = new LocationClient(sContext);
}
if(mMyLocationListener == null) {
mMyLocationListener = new BDLocationListener() {
@Override
public void onReceiveLocation(BDLocation location) {
Log.d(TAG, "DialerApplication.java onReceiveLocation 1111 location retcode = " + location.getLocType() + ",locFlag = " + locFlag);
Log.d("lxm"," DialerApplication.java LocType = "+location.getLocType()+" BDG ="+BDLocation.TypeGpsLocation + " BDN ="+BDLocation.TypeNetWorkLocation);
Log.d("lxm"," DialerApplication.java 111 time ="+ location.getTime()+" Latitude ="+location.getLatitude()+" Longitude ="+location.getLongitude() +" addr ="+location.getAddrStr());
if (location.getLocType() == BDLocation.TypeGpsLocation
|| location.getLocType() == BDLocation.TypeNetWorkLocation) {
mLocationInfo = sContext.getString(R.string.time) + location.getTime()
+ sContext.getString(R.string.latitude) + location.getLatitude()
+ sContext.getString(R.string.lontitude) + location.getLongitude()
+ sContext.getString(R.string.addr) + location.getAddrStr();
Log.d("lxm", "location retcode = " + location.getLocType() + "\n" + mLocationInfo);
stopLocation();
} else if (locFlag > 3) {
mLocationInfo = "";
stopLocation();
} else if (location.getLocType() == BDLocation.TypeNetWorkException) {
locFlag++;
;
RestartLocation();
}
Log.d("lxm", "DialerApplication.java onReceiveLocation 2222 location retcode = " + location.getLocType() + ",locFlag = " + locFlag);
}
@Override
public void onConnectHotSpotMessage(String s , int i){
}
};
}
mLocationClient.registerLocationListener(mMyLocationListener);
mLocationClient.setLocOption(option);
}
public static void startLocation() {
Log.d("lxm", "DialerApplication.java startLocation() 111");
locFlag = 0;
LocationManager locationManager = (LocationManager)sContext.getSystemService(Context.LOCATION_SERVICE);
if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
setLocationMode(android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
}
setMobileDataEnabled();
if(mLocationClient != null) {
Log.d("lxm","DialerApplication.java startLocation() 222");
mLocationClient.start();
mLocationClient.requestLocation();
}
}
public static void stopLocation() {
Log.d(TAG, "DialerApplication.java stopLocation");
locFlag = 3;
if(mLocationClient != null) {
mLocationClient.stop();
}
}
private static void RestartLocation() {
Log.d(TAG, "DialerApplication.java RestartLocation");
if(mLocationClient != null) {
mLocationClient.stop();
mLocationClient.start();
mLocationClient.requestLocation();
}
}
public static int getLocflag() {
return locFlag;
}
public static String getLocationInfo() {
return mLocationInfo;
}
到這裡就介紹完畢了!