最近幾天,因爲項目需要,發彩信界面不再是調用系統的界面,即不跳轉到短信界面,這個功能起初感覺應該很簡單,但是真正做起來後,發現其實沒有想象中那麼簡單,剛做完項目,趁頭腦清晰,先把代碼寫下來先:
這幾天爲了這個項目,網上找了不少資料,大都資料都差不多,對於如何不跳用系統界面發彩信都有講解,但是這個的前提是apn必須是wap,當如果是net時,因爲連接超時而無法發送,正以內發彩信必須通過wap接入點才能發送,即使是在系統界面發彩信時,如果接入點不是wap,則會自動切換過來,待發送完後再切換回去,所以這個模塊的核心,其實就是,如何在調用發彩信時,切換apn至wap,待發送完畢後再切換回去。。。
首先,根據不同移動供應商,需要設置不同的url和proxy
[java] // 電信彩信中心url,代理,端口
public static String mmscUrl_ct = "http://mmsc.vnet.mobi";
public static String mmsProxy_ct = "10.0.0.200";
// 移動彩信中心url,代理,端口
public static String mmscUrl_cm = "http://mmsc.monternet.com";
public static String mmsProxy_cm = "010.000.000.172";
// 聯通彩信中心url,代理,端口
public static String mmscUrl_uni = "http://mmsc.vnet.mobi";
public static String mmsProxy_uni = "10.0.0.172";
// 電信彩信中心url,代理,端口
public static String mmscUrl_ct = "http://mmsc.vnet.mobi";
public static String mmsProxy_ct = "10.0.0.200";
// 移動彩信中心url,代理,端口
public static String mmscUrl_cm = "http://mmsc.monternet.com";
public static String mmsProxy_cm = "010.000.000.172";
// 聯通彩信中心url,代理,端口
public static String mmscUrl_uni = "http://mmsc.vnet.mobi";
public static String mmsProxy_uni = "10.0.0.172";
方法實現如下:
[java] private static List<String> getSimMNC(Context context){
TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String imsi = telManager.getSubscriberId();
if(imsi!=null){
ArrayList<String> list = new ArrayList<String>();
if(imsi.startsWith("46000") ||imsi.startsWith("46002")){
//因爲移動網絡編號46000下的IMSI已經用完,所以虛擬了一個46002編號,134/159號段使用了此編號
//中國移動
list.add(mmscUrl_cm);
list.add(mmsProxy_cm);
}else if(imsi.startsWith("46001")){
//中國聯通
list.add(mmscUrl_uni);
list.add(mmsProxy_uni);
}else if(imsi.startsWith("46003")){
//中國電信
list.add(mmscUrl_ct);
list.add(mmsProxy_ct);
}
shouldChangeApn(context);
return list;
}
return null;
}
private static List<String> getSimMNC(Context context){
TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String imsi = telManager.getSubscriberId();
if(imsi!=null){
ArrayList<String> list = new ArrayList<String>();
if(imsi.startsWith("46000") ||imsi.startsWith("46002")){
//因爲移動網絡編號46000下的IMSI已經用完,所以虛擬了一個46002編號,134/159號段使用了此編號
//中國移動
list.add(mmscUrl_cm);
list.add(mmsProxy_cm);
}else if(imsi.startsWith("46001")){
//中國聯通
list.add(mmscUrl_uni);
list.add(mmsProxy_uni);
}else if(imsi.startsWith("46003")){
//中國電信
list.add(mmscUrl_ct);
list.add(mmsProxy_ct);
}
shouldChangeApn(context);
return list;
}
return null;
}
噹噹前的apn接入點不是發彩信所需要的wap時,就需要切換:
[java] private static boolean shouldChangeApn(final Context context){
final String wapId = getWapApnId(context);
String apnId = getApn(context);
//若當前apn不是wap,則切換至wap
if(!wapId.equals(apnId)){
APN_NET_ID = apnId;
setApn(context,wapId);
//切換apn需要一定時間,先讓等待2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
return false;
}
private static boolean shouldChangeApn(final Context context){
final String wapId = getWapApnId(context);
String apnId = getApn(context);
//若當前apn不是wap,則切換至wap
if(!wapId.equals(apnId)){
APN_NET_ID = apnId;
setApn(context,wapId);
//切換apn需要一定時間,先讓等待2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
return false;
}
所有的apn信息都是存在數據表裏,可以通過adb pull data/data/com.android.providers.telephony d:/ 直接拷出來,不過前提是手機有root權限:[java] private static String getApn(Context context){
ContentResolver resoler = context.getContentResolver();
String[] projection = new String[]{"_id"};
Cursor cur = resoler.query(Uri.parse("content://telephony/carriers/preferapn"),projection, null, null, null);
String apnId = null;
if(cur!=null&&cur.moveToFirst()){
do {
apnId = cur.getString(cur.getColumnIndex("_id"));
} while (cur.moveToNext());
}
return apnId;
}
/**
* 設置接入點
* @param id
*/
private static void setApn(Context context ,String id){
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put("apn_id", id);
resolver.update(uri, values, null, null);
}
private static String APN_NET_ID = null;
/**
* 取到wap接入點的id
* @return
*/
private static String getWapApnId(Context context){
ContentResolver contentResolver = context.getContentResolver();
String[] projection = new String[]{"_id","proxy"};
Cursor cur = contentResolver.query(Uri.parse("content://telephony/carriers"), projection, "current = 1", null, null);
if(cur!=null&&cur.moveToFirst()){
do {
String id = cur.getString(0);
String proxy = cur.getString(1);
if(!TextUtils.isEmpty(proxy)){
return id;
}
} while (cur.moveToNext());
}
return null;
}
private static String getApn(Context context){
ContentResolver resoler = context.getContentResolver();
String[] projection = new String[]{"_id"};
Cursor cur = resoler.query(Uri.parse("content://telephony/carriers/preferapn"),projection, null, null, null);
String apnId = null;
if(cur!=null&&cur.moveToFirst()){
do {
apnId = cur.getString(cur.getColumnIndex("_id"));
} while (cur.moveToNext());
}
return apnId;
}
/**
* 設置接入點
* @param id
*/
private static void setApn(Context context ,String id){
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put("apn_id", id);
resolver.update(uri, values, null, null);
}
private static String APN_NET_ID = null;
/**
* 取到wap接入點的id
* @return
*/
private static String getWapApnId(Context context){
ContentResolver contentResolver = context.getContentResolver();
String[] projection = new String[]{"_id","proxy"};
Cursor cur = contentResolver.query(Uri.parse("content://telephony/carriers"), projection, "current = 1", null, null);
if(cur!=null&&cur.moveToFirst()){
do {
String id = cur.getString(0);
String proxy = cur.getString(1);
if(!TextUtils.isEmpty(proxy)){
return id;
}
} while (cur.moveToNext());
}
return null;
}
取wap接入點的id方法中,就是從carriers表中讀取到current=1的條目,只有這種條目能夠顯示在手機的apn設置中,即:
接下來這個方法,是處理髮彩信請求:
[java] private static boolean sendMMMS(List<String> list,final Context context, byte[] pdu) throws Exception {
// HDR_AVLUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();
if(list==null){
myHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "找不到sim卡", Toast.LENGTH_LONG).show();
}
});
return false;
}
String mmsUrl = (String) list.get(0);
String mmsProxy = (String) list.get(1);
HttpClient client = null;
try {
URI hostUrl = new URI(mmsUrl);
HttpHost target = new HttpHost(hostUrl.getHost(),
hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME);
client = AndroidHttpClient.newInstance("Android-Mms/2.0");
HttpPost post = new HttpPost(mmsUrl);
ByteArrayEntity entity = new ByteArrayEntity(pdu);
entity.setContentType("application/vnd.wap.mms-message");
post.setEntity(entity);
post.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
post.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);
HttpParams params = client.getParams();
HttpProtocolParams.setContentCharset(params, "UTF-8");
ConnRouteParams.setDefaultProxy(params, new HttpHost(mmsProxy,
80));
HttpResponse response = client.execute(target, post);
StatusLine status = response.getStatusLine();
System.out.println("status : " + status.getStatusCode());
if (status.getStatusCode() != 200) {
throw new IOException("HTTP error: " + status.getReasonPhrase());
}
//彩信發送完畢後檢查是否需要把接入點切換回來
if(null!=APN_NET_ID){
setApn(context,APN_NET_ID);
}
return true;
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "彩信發送失敗:"+e.getMessage());
//發送失敗處理
}
return false;
}
private static boolean sendMMMS(List<String> list,final Context context, byte[] pdu) throws Exception {
// HDR_AVLUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();
if(list==null){
myHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "找不到sim卡", Toast.LENGTH_LONG).show();
}
});
return false;
}
String mmsUrl = (String) list.get(0);
String mmsProxy = (String) list.get(1);
HttpClient client = null;
try {
URI hostUrl = new URI(mmsUrl);
HttpHost target = new HttpHost(hostUrl.getHost(),
hostUrl.getPort(), HttpHost.DEFAULT_SCHEME_NAME);
client = AndroidHttpClient.newInstance("Android-Mms/2.0");
HttpPost post = new HttpPost(mmsUrl);
ByteArrayEntity entity = new ByteArrayEntity(pdu);
entity.setContentType("application/vnd.wap.mms-message");
post.setEntity(entity);
post.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);
post.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);
HttpParams params = client.getParams();
HttpProtocolParams.setContentCharset(params, "UTF-8");
ConnRouteParams.setDefaultProxy(params, new HttpHost(mmsProxy,
80));
HttpResponse response = client.execute(target, post);
StatusLine status = response.getStatusLine();
System.out.println("status : " + status.getStatusCode());
if (status.getStatusCode() != 200) {
throw new IOException("HTTP error: " + status.getReasonPhrase());
}
//彩信發送完畢後檢查是否需要把接入點切換回來
if(null!=APN_NET_ID){
setApn(context,APN_NET_ID);
}
return true;
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, "彩信發送失敗:"+e.getMessage());
//發送失敗處理
}
return false;
}
發彩信程序主入口:
[java] private static String HDR_VALUE_ACCEPT_LANGUAGE = "";
private static final String HDR_KEY_ACCEPT = "Accept";
private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";
private static final String HDR_VALUE_ACCEPT = "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";
/**
* 發彩信接口 by liuhanzhi
* @param context
* @param phone 手機號
* @param subject 主題
* @param text 文字
* @param imagePath 圖片路徑
* @param audioPath 音頻路徑
*/
public static void send(final Context context,String phone,String subject,String text,String imagePath,String audioPath) {
// String subject = "測試彩信";
Log.v("MmsTestActivity", subject);
// String recipient = "18911722352";// 138xxxxxxx
SendReq sendRequest = new SendReq();
EncodedStringValue[] sub = EncodedStringValue.extract(subject);
if (sub != null && sub.length > 0) {
sendRequest.setSubject(sub[0]);
}
EncodedStringValue[] phoneNumbers = EncodedStringValue
.extract(phone);
if (phoneNumbers != null && phoneNumbers.length > 0) {
sendRequest.addTo(phoneNumbers[0]);
}
PduBody pduBody = new PduBody();
if(!TextUtils.isEmpty(text)){
PduPart partPdu3 = new PduPart();
partPdu3.setCharset(CharacterSets.UTF_8);
partPdu3.setName("mms_text.txt".getBytes());
partPdu3.setContentType("text/plain".getBytes());
partPdu3.setData(text.getBytes());
pduBody.addPart(partPdu3);
}
if(!TextUtils.isEmpty(imagePath)){
PduPart partPdu = new PduPart();
partPdu.setCharset(CharacterSets.UTF_8);
partPdu.setName("camera.jpg".getBytes());
partPdu.setContentType("image/png".getBytes());
// partPdu.setDataUri(Uri.parse("http://www.2cto.com/uploadfile/2012/0414/20120414101424728.jpg"));
partPdu.setDataUri(Uri.fromFile(new File(imagePath)));
pduBody.addPart(partPdu);
}
if(!TextUtils.isEmpty(audioPath)){
PduPart partPdu2 = new PduPart();
partPdu2.setCharset(CharacterSets.UTF_8);
partPdu2.setName("speech_test.amr".getBytes());
partPdu2.setContentType("audio/amr".getBytes());
// partPdu2.setContentType("audio/amr-wb".getBytes());
// partPdu2.setDataUri(Uri.parse("file://mnt//sdcard//.lv//audio//1326786209801.amr"));
partPdu2.setDataUri(Uri.fromFile(new File(audioPath)));
pduBody.addPart(partPdu2);
}
sendRequest.setBody(pduBody);
final PduComposer composer = new PduComposer(context, sendRequest);
final byte[] bytesToSend = composer.make();
final List<String> list = getSimMNC(context);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
//因爲在切換apn過程中需要一定時間,所以需要加上一個重試操作
int retry = 0;
do {
Log.d(TAG, "重試次數:"+(retry+1));
try {
if (sendMMMS(list, context, bytesToSend)) {
myHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "彩信發送成功!",
Toast.LENGTH_LONG).show();
}
});
return;
}
retry++;
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
} while (retry < 5);
myHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, "彩信發送失敗!", Toast.LENGTH_LONG).show();
}
});
}
});
t.start();
}