android 4.4(KitKat)上,如何开发SMS功能的APP
android4.4(KitKat)上,对SMS功能做了限定,只允许设置为默认短信的APP,才可以写入短信到系统的数据库。此举显然增加了安全性。目前大多数第三方的,需要SMS功能的APP,都可能会有短信不能写入的问题,发布此文时,我测试了下QQ通讯录,仍然不能发送短信,且无法保存短信数据,这都是因为这个KitKatSMS新功能引起的,他们都需要遵循新的开发协定才可以。
下面是我转自blogspot上的一篇文章,阐述了这个问题,原文地址:
http://android-developers.blogspot.fr/2013/10/getting-your-sms-apps-ready-for-kitkat.html
稍后的文章我会阐述如何在android系统层面,解决这个问题。
compatibility.So,toprovideyouwithafullysupportedsetofAPIsforbuildingSMSappsandtomaketheuserexperienceformessagingmorepredictable,Android4.4(KitKat)makestheexistingAPIspublicandaddstheconceptofadefaultSMSapp,whichtheusercanselectinsystemsettings.
ThismeansthatifyouareusingthehiddenSMSAPIsonpreviousplatformversions,youneedtomakesomeadjustmentssoyourappcontinuestoworkwhenAndroid4.4isreleasedlaterthisyear.
MakeyourappthedefaultSMSapp
OnAndroid4.4,onlyoneappcanreceivethenewSMS_DELIVER_ACTIONintent,whichthesystembroadcastswhenanewSMSmessagearrives.WhichappreceivesthisbroadcastisdeterminedbywhichapptheuserhasselectedasthedefaultSMSappinsystemsettings.Likewise,onlythedefaultSMSappreceivesthenewWAP_PUSH_DELIVER_ACTIONintentwhenanewMMSarrives.
OtherappsthatonlywanttoreadnewmessagescaninsteadreceivetheSMS_RECEIVED_ACTIONbroadcastintentwhenanewSMSarrives.However,onlytheappthatreceivestheSMS_DELIVER_ACTIONbroadcast(theuser-specifieddefaultSMSapp)isabletowritetotheSMSProviderdefinedbytheandroid.provider.Telephonyclassandsubclasses.Assuch,it’simportantthatyouupdateyourmessagingappassoonaspossibletobeavailableasadefaultSMSapp,becausealthoughyourexistingappwon’tcrashonanAndroid4.4device,itwillsilentlyfailwhenattemptingtowritetotheSMSProvider.
InorderforyourapptoappearinthesystemsettingsasaneligibledefaultSMSapp,yourmanifestfilemustdeclaresomespecificcapabilities.Soyoumustupdateyourapptodothefollowingthings:
Inabroadcastreceiver,includeanintentfilterforSMS_DELIVER_ACTION("android.provider.Telephony.SMS_DELIVER").ThebroadcastreceivermustalsorequiretheBROADCAST_SMSpermission.ThisallowsyourapptodirectlyreceiveincomingSMSmessages.
Inabroadcastreceiver,includeanintentfilterforWAP_PUSH_DELIVER_ACTION("android.provider.Telephony.WAP_PUSH_DELIVER")withtheMIMEtype"application/vnd.wap.mms-message".ThebroadcastreceivermustalsorequiretheBROADCAST_WAP_PUSHpermission.ThisallowsyourapptodirectlyreceiveincomingMMSmessages.
Inyouractivitythatdeliversnewmessages,includeanintentfilterforACTION_SENDTO("android.intent.action.SENDTO")withschemas,sms:,smsto:,mms:,andmmsto:.Thisallowsyourapptoreceiveintentsfromotherappsthatwanttodeliveramessage.
Inaservice,includeanintentfilterforACTION_RESPONSE_VIA_MESSAGE("android.intent.action.RESPOND_VIA_MESSAGE")withschemas,sms:,smsto:,mms:,andmmsto:.ThisservicemustalsorequiretheSEND_RESPOND_VIA_MESSAGEpermission.Thisallowsuserstorespondtoincomingphonecallswithanimmediatetextmessageusingyourapp.
Forexample,here’samanifestfilewiththenecessarycomponentsandintentfilters:
<manifest>...<application><!--BroadcastReceiverthatlistensforincomingSMSmessages--><receiverandroid:name=".SmsReceiver"android:permission="android.permission.BROADCAST_SMS"><intent-filter><actionandroid:name="android.provider.Telephony.SMS_DELIVER"/></intent-filter></receiver><!--BroadcastReceiverthatlistensforincomingMMSmessages--><receiverandroid:name=".MmsReceiver"android:permission="android.permission.BROADCAST_WAP_PUSH"><intent-filter><actionandroid:name="android.provider.Telephony.WAP_PUSH_DELIVER"/><dataandroid:mimeType="application/vnd.wap.mms-message"/></intent-filter></receiver><!--ActivitythatallowstheusertosendnewSMS/MMSmessages--><activityandroid:name=".ComposeSmsActivity"><intent-filter><actionandroid:name="android.intent.action.SEND"/><actionandroid:name="android.intent.action.SENDTO"/><categoryandroid:name="android.intent.category.DEFAULT"/><categoryandroid:name="android.intent.category.BROWSABLE"/><dataandroid:scheme="sms"/><dataandroid:scheme="smsto"/><dataandroid:scheme="mms"/><dataandroid:scheme="mmsto"/></intent-filter></activity><!--Servicethatdeliversmessagesfromthephone"quickresponse"--><serviceandroid:name=".HeadlessSmsSendService"android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"android:exported="true"><intent-filter><actionandroid:name="android.intent.action.RESPOND_VIA_MESSAGE"/><categoryandroid:name="android.intent.category.DEFAULT"/><dataandroid:scheme="sms"/><dataandroid:scheme="smsto"/><dataandroid:scheme="mms"/><dataandroid:scheme="mmsto"/></intent-filter></service></application></manifest>
AnyfiltersfortheSMS_RECEIVED_ACTIONbroadcastinexistingappswillcontinuetoworkthesameonAndroid4.4,butonlyasanobserverofnewmessages,becauseunlessyourappalsoreceivestheSMS_DELIVER_ACTIONbroadcast,youcannotwritetotheSMSProvideronAndroid4.4.
BeginningwithAndroid4.4,youshouldstoplisteningfortheSMS_RECEIVED_ACTIONbroadcast,whichyoucandoatruntimebycheckingtheplatformversionthendisablingyourbroadcastreceiverforSMS_RECEIVED_ACTIONwithPackageManager.setComponentEnabledSetting().However,youcancontinuelisteningforthatbroadcastifyourappneedsonlytoreadspecialSMSmessages,suchastoperformphonenumberverification.Notethat—beginningwithAndroid4.4—anyattemptbyyourapptoaborttheSMS_RECEIVED_ACTIONbroadcastwillbeignoredsoallappsinterestedhavethechancetoreceiveit.
Tip:TodistinguishthetwoSMSbroadcasts,imaginethattheSMS_RECEIVED_ACTIONsimplysays“thesystemreceivedanSMS,”whereastheSMS_DELIVER_ACTIONsays“thesystemisdeliveringyourappanSMS,becauseyou’rethedefaultSMSapp.”
DisablefeatureswhennotthedefaultSMSapp
InconsiderationofsomeappsthatdonotwanttobehaveasthedefaultSMSappbutstillwanttosendmessages,anyappthathastheSEND_SMSpermissionisstillabletosendSMSmessagesusingSmsManager.IfandonlyifanappisnotselectedasthedefaultSMSapponAndroid4.4,thesystemautomaticallywritesthesentSMSmessagestotheSMSProvider(thedefaultSMSappisalwaysresponsibleforwritingitssentmessagestotheSMSProvider).
However,ifyourappisdesignedtobehaveasthedefaultSMSapp,thenwhileyourappisnotselectedasthedefault,it’simportantthatyouunderstandthelimitationsplaceduponyourappanddisablefeaturesasappropriate.AlthoughthesystemwritessentSMSmessagestotheSMSProviderwhileyourappisnotthedefaultSMSapp,itdoesnotwritesentMMSmessagesandyourappisnotabletowritetotheSMSProviderforotheroperations,suchastomarkmessagesasdraft,markthemasread,deletethem,etc.
Sowhenyourmessagingactivityresumes,checkwhetheryourappisthedefaultSMSappbyqueryingTelephony.Sms.getDefaultSmsPackage(),whichreturnsthepackagenameofthecurrentdefaultSMSapp.Ifitdoesn’tmatchyourpackagename,disablefeaturessuchastheabilityforuserstosendnewmessages.
Whentheuserdecidestouseyourappformessaging,youcandisplayadialoghostedbythesystemthatallowstheusertomakeyourappthedefaultSMSapp.Todisplaythedialog,callstartActivity()withtheTelephony.Sms.Intents.ACTION_CHANGE_DEFAULTintent,includinganextrawiththeSms.Intents.EXTRA_PACKAGE_NAMEkeyandyourpackagenameasthestringvalue.
Forexample,youractivitymightincludecodelikethis:
publicclassComposeSmsActivityextendsActivity{@OverrideprotectedvoidonResume(){super.onResume();finalStringmyPackageName=getPackageName();if(!Telephony.Sms.getDefaultSmsPackage(this).equals(myPackageName)){//Appisnotdefault.//Showthe"notcurrentlysetasthedefaultSMSapp"interfaceViewviewGroup=findViewById(R.id.not_default_app);viewGroup.setVisibility(View.VISIBLE);//SetupabuttonthatallowstheusertochangethedefaultSMSappButtonbutton=(Button)findViewById(R.id.change_default_app);button.setOnClickListener(newView.OnClickListener(){publicvoidonClick(Viewv){Intentintent=newIntent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME,myPackageName);startActivity(intent);}});}else{//Appisthedefault.//Hidethe"notcurrentlysetasthedefaultSMSapp"interfaceViewviewGroup=findViewById(R.id.not_default_app);viewGroup.setVisibility(View.GONE);}}}
AdviceforSMSbackup&restoreapps
BecausetheabilitytowritetotheSMSProviderisrestrictedtotheapptheuserselectsasthedefaultSMSapp,anyexistingappdesignedpurelytobackupandrestoreSMSmessageswillcurrentlybeunabletorestoreSMSmessagesonAndroid4.4.AnappthatbacksupandrestoresSMSmessagesmustalsobesetasthedefaultSMSappsothatitcanwritemessagesintheSMSProvider.However,iftheappdoesnotalsosendandreceiveSMSmessages,thenitshouldnotremainsetasthedefaultSMSapp.So,youcanprovideafunctionaluserexperiencewiththefollowingdesignwhentheuseropensyourapptoinitiateaone-timerestoreoperation:
QuerythecurrentdefaultSMSapp’spackagenameandsaveit.
StringdefaultSmsApp=Telephony.Sms.getDefaultSmsPackage(context);
RequesttheuserchangethedefaultSMSapptoyourappinordertorestoreSMSmessages(youmustbethedefaultSMSappinordertowritetotheSMSProvider).
Intentintent=newIntent(context,Sms.Intents.ACTION_CHANGE_DEFAULT);intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME,context.getPackageName());startActivity(intent);
WhenyoufinishrestoringallSMSmessages,requesttheusertochangethedefaultSMSappbacktothepreviouslyselectedapp(savedduringstep1).
Intentintent=newIntent(context,Sms.Intents.ACTION_CHANGE_DEFAULT);intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME,defaultSmsApp);startActivity(intent);
PreparetoupdateyourSMSapp
WeencourageyoutoupdateyourappsassoonaspossibletoprovideyourusersthebestexperienceonAndroid.Tohelpyoumakethechanges,we’llsoonbeprovidingthenecessarySDKcomponentsforAndroid4.4thatallowyoutocompileandtestyourchangesonAndroid4.4.Staytuned!