1. 程式人生 > >Android SOS功能模組開發

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;
	 }

 

到這裡就介紹完畢了!