当前位置: 编程技术>移动开发
本页文章导读:
▪监听sms收件箱,失去收到短信并置为已读 监听sms收件箱,得到收到短信并置为已读
public class ScreenTest extends Activity {class SmsContent extends ContentObserver{private Cursor cursor = null;public SmsContent(Handler handler) {super(handler);}/*** @Description 当短信.........
▪ Andriod 通话处置流程 Andriod 通话处理流程
Andriod通话处理流程一、总览1、从java端发送at命令的处理流程。2、unsolicited 消息从modem上报到java的流程。3、猫相关的各种状态的监听和通知机制。4、通话相关的图标.........
▪ 自定义控件-CoverFlow 自定义控件-----CoverFlow
CoverFlow--我也不知道为什么要叫这个名字,貌似是从iphone上继承过来的?随便了,反正就是这个样子了此控件的设计和实现思路和部分代码同样是剽窃某网站上的,因为时.........
[1]监听sms收件箱,失去收到短信并置为已读
来源: 互联网 发布时间: 2014-02-18
监听sms收件箱,得到收到短信并置为已读
public class ScreenTest extends Activity {
class SmsContent extends ContentObserver{
private Cursor cursor = null;
public SmsContent(Handler handler) {
super(handler);
}
/**
* @Description 当短信表发送改变时,调用该方法
* 需要两种权限
* android.permission.READ_SMS读取短信
* android.permission.WRITE_SMS写短信
* @Author Snake
* @Date 2010-1-12
*/
@Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
super.onChange(selfChange);
//读取收件箱中指定号码的短信
cursor = managedQuery(Uri.parse(“content://sms/inbox”), new String[]{“_id”, “address”, “read”}, ” address=? and read=?”, new String[]{“12345678901″, “0″}, “date desc”);
if (cursor != null){
ContentValues values = new ContentValues();
values.put(“read”, “1″); //修改短信为已读模式
cursor.moveToFirst();
while (cursor.isLast()){
//更新当前未读短信状态为已读
getContentResolver().update(Uri.parse(“content://sms/inbox”), values, ” _id=?”, new String[]{“”+cursor.getInt(0)});
cursor.moveToNext();
}
}
}
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SmsContent content = new SmsContent(new Handler());
//注册短信变化监听
this.getContentResolver().registerContentObserver(Uri.parse(“content://sms/”), true, content);
}
}
public class ScreenTest extends Activity {
class SmsContent extends ContentObserver{
private Cursor cursor = null;
public SmsContent(Handler handler) {
super(handler);
}
/**
* @Description 当短信表发送改变时,调用该方法
* 需要两种权限
* android.permission.READ_SMS读取短信
* android.permission.WRITE_SMS写短信
* @Author Snake
* @Date 2010-1-12
*/
@Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
super.onChange(selfChange);
//读取收件箱中指定号码的短信
cursor = managedQuery(Uri.parse(“content://sms/inbox”), new String[]{“_id”, “address”, “read”}, ” address=? and read=?”, new String[]{“12345678901″, “0″}, “date desc”);
if (cursor != null){
ContentValues values = new ContentValues();
values.put(“read”, “1″); //修改短信为已读模式
cursor.moveToFirst();
while (cursor.isLast()){
//更新当前未读短信状态为已读
getContentResolver().update(Uri.parse(“content://sms/inbox”), values, ” _id=?”, new String[]{“”+cursor.getInt(0)});
cursor.moveToNext();
}
}
}
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SmsContent content = new SmsContent(new Handler());
//注册短信变化监听
this.getContentResolver().registerContentObserver(Uri.parse(“content://sms/”), true, content);
}
}
[2] Andriod 通话处置流程
来源: 互联网 发布时间: 2014-02-18
Andriod 通话处理流程
Andriod通话处理流程
一、总览
1、从java端发送at命令的处理流程。
2、unsolicited 消息从modem上报到java的流程。
3、猫相关的各种状态的监听和通知机制。
4、通话相关的图标变换的工作原理。
5、gprs拨号上网的通路原理。
6、通话相关的语音通路切换原理、震动接口。
7、通话相关的notification服务。
8、通话相关的各种server。
第一部分:从java端发送at命令的处理流程。
拨出电话流程:
1、contacts的androidmanifest.xml android:process="android.process.acore"说明此应用程序运行在acore进程中。
DialtactsActivity的intent-filter的action属性设置为main,catelog属性设置为launcher,所以此activity能出现在主菜单中,并且是点击此应用程序的第一个界面。dialtactsactivity包含四个tab,分别由TwelveKeyDialer,RecentCallsListActivity,两个activity-alias DialtactsContactsEntryActivity和DialtactsFavoritesEntryActivity分别
表示联系人和收藏tab,但是正真的联系人列表和收藏是由ContactsListActivity负责。
2、进入TwelveKeyDialer OnClick方法,按住的按钮id为: R.id.digits,执行
placecall()
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", number, null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
3、intert.ACTION_CALL_PRIVILEGED实际字符串为android.intent.action.CALL_PRIVILEGED,通过查找知道了packegs/phone
下面的androidmanifest.xml中PrivilegedOutgoingCallBroadcaster activity-alias设置了intent-filter,所以需要找到其
targetactivity为OutgoingCallBroadcaster。所以进入OutgoingCallBroadcaster的
onCreate()
//如果为紧急号码马上启动intent.setClass(this, InCallScreen.class); startActivity(intent);
Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
if (number != null) broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString());
if (LOGV) Log.v(TAG, "Broadcasting intent " + broadcastIntent + ".");
sendOrderedBroadcast(broadcastIntent, PERMISSION, null, null,
Activity.RESULT_OK, number, null);
4、Intent.ACTION_NEW_OUTGOING_CALL实际字符串为android.intent.action.NEW_OUTGOING_CALL,通过查找知道了packegs/phone
下面的androidmanifest.xml中OutgoingCallReceiver Receiver接收此intent消息。找到OutgoingCallReceiver,执行
onReceive()函数
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
newIntent.setClass(context, InCallScreen.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5、请求拨号的java部分流程
onCreate(第一次)/onNewIntent(非第一次)
internalResolveIntent
placeCall(intent);
PhoneUtils.placeCall(mPhone, number, intent.getData());
phone.dial(number);
mCT.dial(newDialString);
dial(dialString, CommandsInterface.CLIR_DEFAULT);
cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());//obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
send(rr);
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
RILSender.handleMessage()
case EVENT_SEND:
... s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);//从这里流程跑到下面ril.cpp中监听部份
6、请求拨号的c/c++部分流程
6.1、初始化事件循环,启动串口监听,注册socket监听。
rild.c->main()
(1)、RIL_startEventLoop
//建立事件循环线程
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
//注册进程唤醒事件回调
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
//建立事件循环
ril_event_loop
for (;;) {
...
n = select(nfds, &rfds, NULL, NULL, ptv);
// Check for timeouts
processTimeouts();
// Check for read-ready
processReadReadies(&rfds, n);
// Fire away
firePending();
}
(2)、funcs = rilInit(&s_rilEnv, argc, rilArgv);//实际是通过动态加载动态库的方式执行reference-ril.c中的RIL_Init
//单独启动一个线程读取串口数据
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
fd = open (s_device_path, O_RDWR);
ret = at_open(fd, onUnsolicited);
ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
在initializeCallback中执行的程序:
setRadioState (RADIO_STATE_OFF);
at_handshake();
/* note: we don't check errors here. Everything important will
be handled in onATTimeout and onATReaderClosed */
/* atchannel is tolerant of echo but it must */
/* have verbose result codes */
at_send_command("ATE0Q0V1", NULL);
/* No auto-answer */
at_send_command("ATS0=0", NULL);
...
//注册rild socket端口事件监听到事件循环中
(3)、RIL_register(funcs);
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
ret = listen(s_fdListen, 4);
ril_event_set (&s_listen_event, s_fdListen, false,
listenCallback, NULL);//将此端口加入事件select队列
rilEventAddWakeup (&s_listen_event);
如果rild socket端口有数据来了将执行listencallback函数
listencallback
//为此客户端连接创建新的监听句柄,s_fdListen继续监听其他客户端的连接。
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
ril_event_set (&s_commands_event, s_fdCommand, 1,
processCommandsCallback, p_rs);//将此端口加入事件select队列
rilEventAddWakeup (&s_commands_event);
6.2、socket监听,收到dial的socket请求
processCommandsCallback
//读数据到p_record中
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
processCommandBuffer(p_record, recordlen);
p.setData((uint8_t *) buffer, buflen);
// status checked at end
status = p.readInt32(&request);
status = p.readInt32 (&token);//请求队列中的序号
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
pRI->token = token;
/*
包含#include "ril_commands.h"语句,结构体如下:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
*/
pRI->pCI = &(s_commands[request]);
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
pRI->pCI->dispatchFunction(p, pRI);
//假设是接收了dial指令,pRI->PCI->dispatchFunction(p,pRI),调用dispatchDial (p,pRI)
dispatchDial (p,pRI)
s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeof(dial), pRI);
in reference-ril.c onRequest()
...
switch (request) {
case RIL_REQUEST_DIAL:
requestDial(data, datalen, t);
asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
ret = at_send_command(cmd, NULL);
err = at_send_command_full (command, NO_RESULT, NULL, NULL, 0, pp_outResponse);
err = at_send_command_full_nolock(command, type, responsePrefix, smspdu,timeoutMsec, sponse);
err = writeline (command);
//此处等待,直到收到成功应答或失败的应答,如:ok,connect,error cme等
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
waiting....
waiting....
/* success or failure is ignored by the upper layer here.it will call GET_CURRENT_CALLS and determine success that way */
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
6.4、串口监听收到atd命令的应答"OK"或"no carrier"等
readerLoop()
line = readline();
processLine(line);
handleFinalResponse(line);
pthread_cond_signal(&s_commandcond);//至此,前面的等待结束,接着执行RIL_onRequestComplete函数
6.5、java层收到应答后的处理,以dial为例子.
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
type = p.readInt();
if (type == RESPONSE_SOLICITED) {
processSolicited (p);
serial = p.readInt();
rr = findAndRemoveRequestFromList(serial);
rr.mResult.sendToTarget();
......
}
CallTracker.java->handleMessage (Message msg)
switch (msg.what) {
case EVENT_OPERATION_COMPLETE:
ar = (AsyncResult)msg.obj;
operationComplete();
cm.getCurrentCalls(lastRelevantPoll);
第二部分:unsolicited 消息从modem上报到java的流程。
c++部份
readerLoop()
line = readline();
processLine(line);
handleUnsolicited(line);
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);//实际执行的是void onUnsolicited (const char *s, const char *sms_pdu)
if (strStartsWith(s,"+CRING:")
|| strStartsWith(s,"RING")
|| strStartsWith(s,"NO CARRIER")
|| strStartsWith(s,"+CCWA")
)
RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0);
p.writeInt32 (RESPONSE_UNSOLICITED);
p.writeInt32 (unsolResponse);
ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen);
ret = sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
java部份
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processUnsolicited (p);
response = p.readInt();
switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
...
}
switch(response) {
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
mCallStateRegistrants
.notifyRegistrants(new AsyncResult(null, null, null));
...
}
第三部分、第四部分:猫相关的各种状态的监听和通知机制/通话相关的图标变换的工作原理。
网络状态,edge,gprs图标的处理
a、注册监听部分
==>SystemServer.java
init2()
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
ServerThread.run() com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
sInstance = new StatusBarPolicy(context, service);
// phone_signal
mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
mPhoneData = IconData.makeIcon("phone_signal",
null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
mPhoneIcon = service.addIcon(mPhoneData, null);
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTH
| PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
//实际是调用的是TelephonyRegistry.listen,此listen函数会将Iphonestatelistener添加到对应的的handler数组中,到时来了事件会轮询回调。
// data_connection
mDataData = IconData.makeIcon("data_connection", null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
mDataIcon = service.addIcon(mDataData, null);
service.setIconVisibility(mDataIcon, false);
b、事件通知部分
==>PhoneFactory.java
makeDefaultPhones()
sPhoneNotifier = new DefaultPhoneNotifier();
useNewRIL(context);
phone = new GSMPhone(context, new RIL(context), sPhoneNotifier);
for example
==>DataConnectionTracker.java
notifyDefaultData(String reason)
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
==>DefaultPhoneNotifier.java
mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));
第五部分:gprs拨号上网的通路原理。
上层java程序调用gprs流程:
=>PhoneApp.java
onCreate()
PhoneFactory.makeDefaultPhones(this);
phone = new GSMPhone(context, new SimulatedCommands(), sPhoneNotifier);
mDataConnection = new DataConnectionTracker (this);
createAllPdpList();//建立缺省pdpconnection
pdp = new PdpConnection(phone);
dataLink = new PppLink(phone.mDataConnection);
dataLink.setOnLinkChange(this, EVENT_LINK_STATE_CHANGED, null);
//某个条件触发执行
trySetupData(String reason)
setupData(reason);
pdp = findFreePdp();
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
msg.obj = reason;
pdp.connect(apn, msg);
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));
//收到EVENT_SETUP_PDP_DONE消息
=>pdpconnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
dataLink.connect();//dataLink是pppLink.java
SystemService.start(SERVICE_PPPD_GPRS);//启动pppd_grps服务
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);//启动轮询,看是否成功连接gprscheckPPP()//每隔5秒轮询,看是否连接成功,或断开
//如果已经连接
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
//执行到pdpconnection.handleMessage()
case EVENT_LINK_STATE_CHANGED
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
onCompleted.sendToTarget();
//执行dataConnectionTracker.java的handleMessage()
case EVENT_DATA_SETUP_COMPLETE
notifyDefaultData(reason);
setupDnsProperties();
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
startNetStatPoll();
resetPollStats();
1、读取发送出去的包数和接受到的包数
2、如果发送的数据包且没有收到应答包数n大于等于看门狗追踪的限定包数。
2.1、开始轮询pdp context list,尝试恢复网络连接
2.2、如果轮询24次后还没有联通网络则停止网络状态轮询,进行一次ping实验。
2.2.1、如果ping成功则,重新进行网络状态轮询,否则发送EVENT_START_RECOVERY事件。
// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
着重c++部分代码的角度分析
=>DataConnectionTracker.java
trySetupData(String reason)
setupData(reason);
=>PdpConnection.java
pdp.connect(apn, msg);
=>RIL.JAVA
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));
send(rr);
//send socket to RIL
//enter c++ layer
=>ril.cpp
processCommandsCallback (int fd, short flags, void *param)
processCommandBuffer(p_record, recordlen);
status = p.readInt32(&request);
pRI->pCI = &(s_commands[request]);
pRI->pCI->dispatchFunction(p, pRI);
dispatchStrings();
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);
=>reference-ril.c
onRequest();
requestSetupDefaultPDP(data, datalen, t);
err = write_at_to_data_channel("ATD*99***1#",1);
//after a while.get "connect" from data channel,so need to send socket message to java layer.
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);//the serial No in the request list.
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
/* if an error occurred, rewind and mark it */
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
=>RIL.JAVA
RILReceiver.run();
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processSolicited (p);
serial = p.readInt();
error = p.readInt();
rr = findAndRemoveRequestFromList(serial);
ret = responseStrings(p);
if (rr.mResult != null) {
AsyncResult.forMessage(rr.mResult, ret, null);
rr.mResult.sendToTarget();
}
=>pdpConnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
...
dataLink.connect();
=>pppLink.java
SystemProperties.set(PROPERTY_PPPD_EXIT_CODE, "");
SystemService.start(SERVICE_PPPD_GPRS);//启动pppd_grps服务
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);
dataConnection.state = State.CONNECTING;
handleMessage()
case EVENT_POLL_DATA_CONNECTION
checkPPP();
if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length)
|| ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING,
UNKNOWN_ASCII_STRING.length) && dataConnection.state == State.CONNECTING)
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
=>pdpConnection.java
handleMessage()
case EVENT_LINK_STATE_CHANGED:
DataLink.LinkState ls = (DataLink.LinkState) ar.result;
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
AsyncResult.forMessage(onCompleted);
onCompleted.sendToTarget();
=>DataConnectionTracker.java
handleMessage()
case EVENT_DATA_SETUP_COMPLETE:
... SystemProperties.set("gsm.defaultpdpcontext.active", "true");
notifyDefaultData(reason);
setupDnsProperties();//设置dns,gw,我们的实现方式是在pppd中设置的,不用pppd拨号的适用。
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
=>DefaultPhoneNotifier.java
//mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));构造函数中初始化了mRegistry mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()), sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));
startNetStatPoll();
}
第六部分:通话相关的语音通路切换原理、震动接口
6、语音通路
6.1、设置语音通路的路由
目前我们有两处处理:
a、CallTracker.java中的handlePollCalls()检测到+clcc返回的电话列表中有状态为DriverCall.State.ALERTING(表示拨打电话后,对方已经振铃),此时需要设置语音通路为MODE_IN_CALL
b、PhoneUtils.java中setAudioMode()函数
c、调用通路分析
AudioManager audioManager = (AudioManager) context.getSystemService
(Context.AUDIO_SERVICE);
audioManager.setMode(mode);
AudioManager.setMode(mode);
AudioService.setMode(mode);
AudioSystem.setMode(mode);(native function)
android_media_AudioSystem.cpp==>android_media_AudioSystem_setMode()
AudioSystem.cpp==>setMode()
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
binder = sm->getService(String16("media.audio_flinger"));
...
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
...
return gAudioFlinger;
通过查找“media.audio_flinger”发现AudioFlinger.cpp==>instantiate()//Main_mediaserver.cpp中被实例化。 defaultServiceManager()->addService(String16("media.audio_flinger"), new AudioFlinger());
mAudioHardware = AudioHardwareInterface::create();
LOGV("Creating Vendor Specific AudioHardware");
hw = createAudioHardware();
return new AudioHardwareMarvell();
return af->setMode(mode);
AudioHardwareLittle.cpp==>setMode(mode)
doRouting();
enable_incall_headphone()//or others...
system("alsactl -f /etc/alsactl/asound.state_none restore");
system("alsactl -f /etc/alsactl/asound.state_headset_r_s restore");
6.2、来电播放振铃,挂断或接听停止振铃。
==>Phone.app
onCreate()
ringer = new Ringer(phone);
Vibrator mVibrator = new Vibrator();
mService = IHardwareService.Stub.asInterface(ServiceManager.getService("hardware"));
notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);
mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null);
mPhone.registerForPhoneStateChanged(this, PHONE_STATE_CHANGED, null);
mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null);
...
case PHONE_INCOMING_RING:
mRinger.ring();
mHardwareService.setAttentionLight(true);
mVibratorThread.start();
while (mContinueVibrating) {
mVibrator.vibrate(VIBRATE_LENGTH);
SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH);
}
...
makeLooper();
mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);
...
case PLAY_RING_ONCE:
PhoneUtils.setAudioMode(mContext,AudioManager.MODE_RINGTONE);
r.play();
...
case PHONE_DISCONNECT:
case PHONE_STATE_CHANGED:
...
mRinger.stopRing();
Message msg = mRingHandler.obtainMessage(STOP_RING);
msg.obj = mRingtone;
mRingHandler.sendMessage(msg);
case STOP_RING:
r.stop();
getLooper().quit();
...mVibrator.cancel();
第七部分:通话相关的notification服务
7、通话相关的notification服务。
7.1、NotificationMgr
==>PhoneApp.java
onCreate()
NotificationMgr.init(this)//NotificationMgr.java//此类主要负责电话通知的具体表现(通知和取消通知),未接图标、通话中、蓝牙激活中、保持中,静音、免提等。封装了简单的瞬间显示文本消息的功能。提供漫游数据连接禁止的通知封装和漫游数据连接允许时取消通知
sMe = new NotificationMgr(context);
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
mStatusBar = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); //主要用于显示静音和speaker状态的图表(在状态条右边显示)
sMe.updateNotifications();//主要功能是:
1、查询是否有未读的未接听电话,并显示到状态栏图标,和通知列表
2、根据是否是电话状态,更新状态栏图表和通知列表(可能是激活,蓝牙,保持等)
7.2、CallNotifier
==>PhoneApp.java
onCreate()
notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);//此类主要是监听通话相关的事件,然后进行例如来电播放铃声,震动。挂断、接听停止振铃等(调用Ringer类实现此功能),根据不同的状态调用调用NotificationMgr进行具体的通知和取消通知。
第八部分: 通话相关的各种server
电话通信相关的服务:
(1)、从ServiceManager得到的:
a、wifiService
b、PhoneInterfaceManager
c、PhoneSubInfo
d、SimPhoneBookInterfaceManager
e、SimSmsInterfaceManager
f、TelephonyRegistry
g、NetStatService
h、ConnectivityService
(2)、从ApplicationContext得到的:
a、TelephonyManager
呵呵!关于incallscreen界面如何改变的可以看一下
InCallScreen.java这个类中的handle!里面有注册PHONE_STATE_CHANGED等一系列的事件!updateScreen()实际就是界面更新了!
还有底层时间的通知和电话状态的判断大部分处理实际在CallCard.java里面的!
另外你做的是哪个版本的是2.2或2.3还是3.0/3.1!
Andriod通话处理流程
一、总览
1、从java端发送at命令的处理流程。
2、unsolicited 消息从modem上报到java的流程。
3、猫相关的各种状态的监听和通知机制。
4、通话相关的图标变换的工作原理。
5、gprs拨号上网的通路原理。
6、通话相关的语音通路切换原理、震动接口。
7、通话相关的notification服务。
8、通话相关的各种server。
第一部分:从java端发送at命令的处理流程。
拨出电话流程:
1、contacts的androidmanifest.xml android:process="android.process.acore"说明此应用程序运行在acore进程中。
DialtactsActivity的intent-filter的action属性设置为main,catelog属性设置为launcher,所以此activity能出现在主菜单中,并且是点击此应用程序的第一个界面。dialtactsactivity包含四个tab,分别由TwelveKeyDialer,RecentCallsListActivity,两个activity-alias DialtactsContactsEntryActivity和DialtactsFavoritesEntryActivity分别
表示联系人和收藏tab,但是正真的联系人列表和收藏是由ContactsListActivity负责。
2、进入TwelveKeyDialer OnClick方法,按住的按钮id为: R.id.digits,执行
placecall()
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", number, null));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
3、intert.ACTION_CALL_PRIVILEGED实际字符串为android.intent.action.CALL_PRIVILEGED,通过查找知道了packegs/phone
下面的androidmanifest.xml中PrivilegedOutgoingCallBroadcaster activity-alias设置了intent-filter,所以需要找到其
targetactivity为OutgoingCallBroadcaster。所以进入OutgoingCallBroadcaster的
onCreate()
//如果为紧急号码马上启动intent.setClass(this, InCallScreen.class); startActivity(intent);
Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
if (number != null) broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, intent.getData().toString());
if (LOGV) Log.v(TAG, "Broadcasting intent " + broadcastIntent + ".");
sendOrderedBroadcast(broadcastIntent, PERMISSION, null, null,
Activity.RESULT_OK, number, null);
4、Intent.ACTION_NEW_OUTGOING_CALL实际字符串为android.intent.action.NEW_OUTGOING_CALL,通过查找知道了packegs/phone
下面的androidmanifest.xml中OutgoingCallReceiver Receiver接收此intent消息。找到OutgoingCallReceiver,执行
onReceive()函数
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
newIntent.setClass(context, InCallScreen.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5、请求拨号的java部分流程
onCreate(第一次)/onNewIntent(非第一次)
internalResolveIntent
placeCall(intent);
PhoneUtils.placeCall(mPhone, number, intent.getData());
phone.dial(number);
mCT.dial(newDialString);
dial(dialString, CommandsInterface.CLIR_DEFAULT);
cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());//obtainCompleteMessage(EVENT_OPERATION_COMPLETE);
send(rr);
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
RILSender.handleMessage()
case EVENT_SEND:
... s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);//从这里流程跑到下面ril.cpp中监听部份
6、请求拨号的c/c++部分流程
6.1、初始化事件循环,启动串口监听,注册socket监听。
rild.c->main()
(1)、RIL_startEventLoop
//建立事件循环线程
ret = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
//注册进程唤醒事件回调
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
//建立事件循环
ril_event_loop
for (;;) {
...
n = select(nfds, &rfds, NULL, NULL, ptv);
// Check for timeouts
processTimeouts();
// Check for read-ready
processReadReadies(&rfds, n);
// Fire away
firePending();
}
(2)、funcs = rilInit(&s_rilEnv, argc, rilArgv);//实际是通过动态加载动态库的方式执行reference-ril.c中的RIL_Init
//单独启动一个线程读取串口数据
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
fd = open (s_device_path, O_RDWR);
ret = at_open(fd, onUnsolicited);
ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
在initializeCallback中执行的程序:
setRadioState (RADIO_STATE_OFF);
at_handshake();
/* note: we don't check errors here. Everything important will
be handled in onATTimeout and onATReaderClosed */
/* atchannel is tolerant of echo but it must */
/* have verbose result codes */
at_send_command("ATE0Q0V1", NULL);
/* No auto-answer */
at_send_command("ATS0=0", NULL);
...
//注册rild socket端口事件监听到事件循环中
(3)、RIL_register(funcs);
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
ret = listen(s_fdListen, 4);
ril_event_set (&s_listen_event, s_fdListen, false,
listenCallback, NULL);//将此端口加入事件select队列
rilEventAddWakeup (&s_listen_event);
如果rild socket端口有数据来了将执行listencallback函数
listencallback
//为此客户端连接创建新的监听句柄,s_fdListen继续监听其他客户端的连接。
s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);
ril_event_set (&s_commands_event, s_fdCommand, 1,
processCommandsCallback, p_rs);//将此端口加入事件select队列
rilEventAddWakeup (&s_commands_event);
6.2、socket监听,收到dial的socket请求
processCommandsCallback
//读数据到p_record中
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
processCommandBuffer(p_record, recordlen);
p.setData((uint8_t *) buffer, buflen);
// status checked at end
status = p.readInt32(&request);
status = p.readInt32 (&token);//请求队列中的序号
pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));
pRI->token = token;
/*
包含#include "ril_commands.h"语句,结构体如下:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} CommandInfo;
*/
pRI->pCI = &(s_commands[request]);
pRI->p_next = s_pendingRequests;
s_pendingRequests = pRI;
pRI->pCI->dispatchFunction(p, pRI);
//假设是接收了dial指令,pRI->PCI->dispatchFunction(p,pRI),调用dispatchDial (p,pRI)
dispatchDial (p,pRI)
s_callbacks.onRequest(pRI->pCI->requestNumber, &dial, sizeof(dial), pRI);
in reference-ril.c onRequest()
...
switch (request) {
case RIL_REQUEST_DIAL:
requestDial(data, datalen, t);
asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
ret = at_send_command(cmd, NULL);
err = at_send_command_full (command, NO_RESULT, NULL, NULL, 0, pp_outResponse);
err = at_send_command_full_nolock(command, type, responsePrefix, smspdu,timeoutMsec, sponse);
err = writeline (command);
//此处等待,直到收到成功应答或失败的应答,如:ok,connect,error cme等
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
waiting....
waiting....
/* success or failure is ignored by the upper layer here.it will call GET_CURRENT_CALLS and determine success that way */
RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
6.4、串口监听收到atd命令的应答"OK"或"no carrier"等
readerLoop()
line = readline();
processLine(line);
handleFinalResponse(line);
pthread_cond_signal(&s_commandcond);//至此,前面的等待结束,接着执行RIL_onRequestComplete函数
6.5、java层收到应答后的处理,以dial为例子.
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
type = p.readInt();
if (type == RESPONSE_SOLICITED) {
processSolicited (p);
serial = p.readInt();
rr = findAndRemoveRequestFromList(serial);
rr.mResult.sendToTarget();
......
}
CallTracker.java->handleMessage (Message msg)
switch (msg.what) {
case EVENT_OPERATION_COMPLETE:
ar = (AsyncResult)msg.obj;
operationComplete();
cm.getCurrentCalls(lastRelevantPoll);
第二部分:unsolicited 消息从modem上报到java的流程。
c++部份
readerLoop()
line = readline();
processLine(line);
handleUnsolicited(line);
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);//实际执行的是void onUnsolicited (const char *s, const char *sms_pdu)
if (strStartsWith(s,"+CRING:")
|| strStartsWith(s,"RING")
|| strStartsWith(s,"NO CARRIER")
|| strStartsWith(s,"+CCWA")
)
RIL_onUnsolicitedResponse (RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0);
p.writeInt32 (RESPONSE_UNSOLICITED);
p.writeInt32 (unsolResponse);
ret = s_unsolResponses[unsolResponseIndex].responseFunction(p, data, datalen);
ret = sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
java部份
ril.java->RILReceiver.run()
for(;;)
{
...
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processUnsolicited (p);
response = p.readInt();
switch(response) {
...
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
...
}
switch(response) {
case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
if (RILJ_LOGD) unsljLog(response);
mCallStateRegistrants
.notifyRegistrants(new AsyncResult(null, null, null));
...
}
第三部分、第四部分:猫相关的各种状态的监听和通知机制/通话相关的图标变换的工作原理。
网络状态,edge,gprs图标的处理
a、注册监听部分
==>SystemServer.java
init2()
Thread thr = new ServerThread();
thr.setName("android.server.ServerThread");
thr.start();
ServerThread.run() com.android.server.status.StatusBarPolicy.installIcons(context, statusBar);
sInstance = new StatusBarPolicy(context, service);
// phone_signal
mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
mPhoneData = IconData.makeIcon("phone_signal",
null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
mPhoneIcon = service.addIcon(mPhoneData, null);
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SERVICE_STATE
| PhoneStateListener.LISTEN_SIGNAL_STRENGTH
| PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY);
//实际是调用的是TelephonyRegistry.listen,此listen函数会将Iphonestatelistener添加到对应的的handler数组中,到时来了事件会轮询回调。
// data_connection
mDataData = IconData.makeIcon("data_connection", null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
mDataIcon = service.addIcon(mDataData, null);
service.setIconVisibility(mDataIcon, false);
b、事件通知部分
==>PhoneFactory.java
makeDefaultPhones()
sPhoneNotifier = new DefaultPhoneNotifier();
useNewRIL(context);
phone = new GSMPhone(context, new RIL(context), sPhoneNotifier);
for example
==>DataConnectionTracker.java
notifyDefaultData(String reason)
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
==>DefaultPhoneNotifier.java
mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));
第五部分:gprs拨号上网的通路原理。
上层java程序调用gprs流程:
=>PhoneApp.java
onCreate()
PhoneFactory.makeDefaultPhones(this);
phone = new GSMPhone(context, new SimulatedCommands(), sPhoneNotifier);
mDataConnection = new DataConnectionTracker (this);
createAllPdpList();//建立缺省pdpconnection
pdp = new PdpConnection(phone);
dataLink = new PppLink(phone.mDataConnection);
dataLink.setOnLinkChange(this, EVENT_LINK_STATE_CHANGED, null);
//某个条件触发执行
trySetupData(String reason)
setupData(reason);
pdp = findFreePdp();
Message msg = obtainMessage();
msg.what = EVENT_DATA_SETUP_COMPLETE;
msg.obj = reason;
pdp.connect(apn, msg);
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));
//收到EVENT_SETUP_PDP_DONE消息
=>pdpconnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
dataLink.connect();//dataLink是pppLink.java
SystemService.start(SERVICE_PPPD_GPRS);//启动pppd_grps服务
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);//启动轮询,看是否成功连接gprscheckPPP()//每隔5秒轮询,看是否连接成功,或断开
//如果已经连接
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
//执行到pdpconnection.handleMessage()
case EVENT_LINK_STATE_CHANGED
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
onCompleted.sendToTarget();
//执行dataConnectionTracker.java的handleMessage()
case EVENT_DATA_SETUP_COMPLETE
notifyDefaultData(reason);
setupDnsProperties();
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
startNetStatPoll();
resetPollStats();
1、读取发送出去的包数和接受到的包数
2、如果发送的数据包且没有收到应答包数n大于等于看门狗追踪的限定包数。
2.1、开始轮询pdp context list,尝试恢复网络连接
2.2、如果轮询24次后还没有联通网络则停止网络状态轮询,进行一次ping实验。
2.2.1、如果ping成功则,重新进行网络状态轮询,否则发送EVENT_START_RECOVERY事件。
// reset reconnect timer
nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
着重c++部分代码的角度分析
=>DataConnectionTracker.java
trySetupData(String reason)
setupData(reason);
=>PdpConnection.java
pdp.connect(apn, msg);
=>RIL.JAVA
phone.mCM.setupDefaultPDP(apn.apn, apn.user, apn.password,
obtainMessage(EVENT_SETUP_PDP_DONE));
send(rr);
//send socket to RIL
//enter c++ layer
=>ril.cpp
processCommandsCallback (int fd, short flags, void *param)
processCommandBuffer(p_record, recordlen);
status = p.readInt32(&request);
pRI->pCI = &(s_commands[request]);
pRI->pCI->dispatchFunction(p, pRI);
dispatchStrings();
s_callbacks.onRequest(pRI->pCI->requestNumber, pStrings, datalen, pRI);
=>reference-ril.c
onRequest();
requestSetupDefaultPDP(data, datalen, t);
err = write_at_to_data_channel("ATD*99***1#",1);
//after a while.get "connect" from data channel,so need to send socket message to java layer.
p.writeInt32 (RESPONSE_SOLICITED);
p.writeInt32 (pRI->token);//the serial No in the request list.
errorOffset = p.dataPosition();
p.writeInt32 (e);
if (e == RIL_E_SUCCESS) {
/* process response on success */
ret = pRI->pCI->responseFunction(p, response, responselen);
/* if an error occurred, rewind and mark it */
if (ret != 0) {
p.setDataPosition(errorOffset);
p.writeInt32 (ret);
}
}
sendResponse(p);
sendResponseRaw(p.data(), p.dataSize());
ret = blockingWrite(fd, (void *)&header, sizeof(header));
blockingWrite(fd, data, dataSize);
=>RIL.JAVA
RILReceiver.run();
length = readRilMessage(is, buffer);
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p);
processSolicited (p);
serial = p.readInt();
error = p.readInt();
rr = findAndRemoveRequestFromList(serial);
ret = responseStrings(p);
if (rr.mResult != null) {
AsyncResult.forMessage(rr.mResult, ret, null);
rr.mResult.sendToTarget();
}
=>pdpConnection.java
handleMessage()
case EVENT_SETUP_PDP_DONE:
...
dataLink.connect();
=>pppLink.java
SystemProperties.set(PROPERTY_PPPD_EXIT_CODE, "");
SystemService.start(SERVICE_PPPD_GPRS);//启动pppd_grps服务
poll.what = EVENT_POLL_DATA_CONNECTION;
sendMessageDelayed(poll, POLL_SYSFS_MILLIS);
dataConnection.state = State.CONNECTING;
handleMessage()
case EVENT_POLL_DATA_CONNECTION
checkPPP();
if (ArrayUtils.equals(mCheckPPPBuffer, UP_ASCII_STRING, UP_ASCII_STRING.length)
|| ArrayUtils.equals(mCheckPPPBuffer, UNKNOWN_ASCII_STRING,
UNKNOWN_ASCII_STRING.length) && dataConnection.state == State.CONNECTING)
if (mLinkChangeRegistrant != null) {
mLinkChangeRegistrant.notifyResult(LinkState.LINK_UP);
=>pdpConnection.java
handleMessage()
case EVENT_LINK_STATE_CHANGED:
DataLink.LinkState ls = (DataLink.LinkState) ar.result;
onLinkStateChanged(ls);
case LINK_UP:
notifySuccess(onConnectCompleted);
AsyncResult.forMessage(onCompleted);
onCompleted.sendToTarget();
=>DataConnectionTracker.java
handleMessage()
case EVENT_DATA_SETUP_COMPLETE:
... SystemProperties.set("gsm.defaultpdpcontext.active", "true");
notifyDefaultData(reason);
setupDnsProperties();//设置dns,gw,我们的实现方式是在pppd中设置的,不用pppd拨号的适用。
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
mNotifier.notifyDataConnection(this, reason);
=>DefaultPhoneNotifier.java
//mRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));构造函数中初始化了mRegistry mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()), sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
sender.getInterfaceName(null));
startNetStatPoll();
}
第六部分:通话相关的语音通路切换原理、震动接口
6、语音通路
6.1、设置语音通路的路由
目前我们有两处处理:
a、CallTracker.java中的handlePollCalls()检测到+clcc返回的电话列表中有状态为DriverCall.State.ALERTING(表示拨打电话后,对方已经振铃),此时需要设置语音通路为MODE_IN_CALL
b、PhoneUtils.java中setAudioMode()函数
c、调用通路分析
AudioManager audioManager = (AudioManager) context.getSystemService
(Context.AUDIO_SERVICE);
audioManager.setMode(mode);
AudioManager.setMode(mode);
AudioService.setMode(mode);
AudioSystem.setMode(mode);(native function)
android_media_AudioSystem.cpp==>android_media_AudioSystem_setMode()
AudioSystem.cpp==>setMode()
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
binder = sm->getService(String16("media.audio_flinger"));
...
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
...
return gAudioFlinger;
通过查找“media.audio_flinger”发现AudioFlinger.cpp==>instantiate()//Main_mediaserver.cpp中被实例化。 defaultServiceManager()->addService(String16("media.audio_flinger"), new AudioFlinger());
mAudioHardware = AudioHardwareInterface::create();
LOGV("Creating Vendor Specific AudioHardware");
hw = createAudioHardware();
return new AudioHardwareMarvell();
return af->setMode(mode);
AudioHardwareLittle.cpp==>setMode(mode)
doRouting();
enable_incall_headphone()//or others...
system("alsactl -f /etc/alsactl/asound.state_none restore");
system("alsactl -f /etc/alsactl/asound.state_headset_r_s restore");
6.2、来电播放振铃,挂断或接听停止振铃。
==>Phone.app
onCreate()
ringer = new Ringer(phone);
Vibrator mVibrator = new Vibrator();
mService = IHardwareService.Stub.asInterface(ServiceManager.getService("hardware"));
notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);
mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null);
mPhone.registerForPhoneStateChanged(this, PHONE_STATE_CHANGED, null);
mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null);
...
case PHONE_INCOMING_RING:
mRinger.ring();
mHardwareService.setAttentionLight(true);
mVibratorThread.start();
while (mContinueVibrating) {
mVibrator.vibrate(VIBRATE_LENGTH);
SystemClock.sleep(VIBRATE_LENGTH + PAUSE_LENGTH);
}
...
makeLooper();
mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);
...
case PLAY_RING_ONCE:
PhoneUtils.setAudioMode(mContext,AudioManager.MODE_RINGTONE);
r.play();
...
case PHONE_DISCONNECT:
case PHONE_STATE_CHANGED:
...
mRinger.stopRing();
Message msg = mRingHandler.obtainMessage(STOP_RING);
msg.obj = mRingtone;
mRingHandler.sendMessage(msg);
case STOP_RING:
r.stop();
getLooper().quit();
...mVibrator.cancel();
第七部分:通话相关的notification服务
7、通话相关的notification服务。
7.1、NotificationMgr
==>PhoneApp.java
onCreate()
NotificationMgr.init(this)//NotificationMgr.java//此类主要负责电话通知的具体表现(通知和取消通知),未接图标、通话中、蓝牙激活中、保持中,静音、免提等。封装了简单的瞬间显示文本消息的功能。提供漫游数据连接禁止的通知封装和漫游数据连接允许时取消通知
sMe = new NotificationMgr(context);
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
mStatusBar = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); //主要用于显示静音和speaker状态的图表(在状态条右边显示)
sMe.updateNotifications();//主要功能是:
1、查询是否有未读的未接听电话,并显示到状态栏图标,和通知列表
2、根据是否是电话状态,更新状态栏图表和通知列表(可能是激活,蓝牙,保持等)
7.2、CallNotifier
==>PhoneApp.java
onCreate()
notifier = new CallNotifier(this, phone, ringer, mBtHandsfree);//此类主要是监听通话相关的事件,然后进行例如来电播放铃声,震动。挂断、接听停止振铃等(调用Ringer类实现此功能),根据不同的状态调用调用NotificationMgr进行具体的通知和取消通知。
第八部分: 通话相关的各种server
电话通信相关的服务:
(1)、从ServiceManager得到的:
a、wifiService
b、PhoneInterfaceManager
c、PhoneSubInfo
d、SimPhoneBookInterfaceManager
e、SimSmsInterfaceManager
f、TelephonyRegistry
g、NetStatService
h、ConnectivityService
(2)、从ApplicationContext得到的:
a、TelephonyManager
1 楼
xwangly
2011-06-02
第三部分、第四部分:猫相关的各种状态的监听和通知机制/通话相关的图标变换的工作原理。
a、注册监听部分
这里一直没看明白
1。
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen
这段代码一直没找到,不知道哪些监听器加入了监听
2。底层的状态改变往上通知,跟踪代码走到了TelephoneRegistry中,然后调用已经加入监听的对象的回调方法,该回调方法会调用PhoneStateListener类的onCallStateChange方法。这个方法是个空实现,那么需要注册监听的类是不是要重写这个方法呢?可是我也没找到继承PhoneStateListener的类。
根本原因 ,我不知道哪些对象在哪注册了监听。望指教。
a、注册监听部分
这里一直没看明白
1。
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)).listen
这段代码一直没找到,不知道哪些监听器加入了监听
2。底层的状态改变往上通知,跟踪代码走到了TelephoneRegistry中,然后调用已经加入监听的对象的回调方法,该回调方法会调用PhoneStateListener类的onCallStateChange方法。这个方法是个空实现,那么需要注册监听的类是不是要重写这个方法呢?可是我也没找到继承PhoneStateListener的类。
根本原因 ,我不知道哪些对象在哪注册了监听。望指教。
2 楼
xwangly
2011-06-02
事件通知部分,一直没找到底层是如何通知到InCalScreen来改变界面的。
请指教
请指教
3 楼
yelinsen05
2011-06-07
xwangly 写道
事件通知部分,一直没找到底层是如何通知到InCalScreen来改变界面的。
请指教
请指教
呵呵!关于incallscreen界面如何改变的可以看一下
InCallScreen.java这个类中的handle!里面有注册PHONE_STATE_CHANGED等一系列的事件!updateScreen()实际就是界面更新了!
还有底层时间的通知和电话状态的判断大部分处理实际在CallCard.java里面的!
另外你做的是哪个版本的是2.2或2.3还是3.0/3.1!
[3] 自定义控件-CoverFlow
来源: 互联网 发布时间: 2014-02-18
自定义控件-----CoverFlow
CoverFlow--我也不知道为什么要叫这个名字,貌似是从iphone上继承过来的?
随便了,反正就是这个样子了
此控件的设计和实现思路和部分代码同样是剽窃某网站上的,因为时间太久了,找不到原文地址了..杯具,所以我才来做备份的.
此类是从Gallery继承过来的,用法自然也就和Gallery一样了
程序的背景是一个xml陪的渐变背景,具体可以参看另外一篇"渐变背景"的文章
图片的倒影效果是适用了ReflectionImage控件,具体可以参看上一篇博文.
先看代码吧,看了代码什么都明白了
view plaincopy to clipboardprint?
package com.myview;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.ImageView;
public class CoverFlow extends Gallery {
private Camera mCamera = new Camera();
private int mMaxRotationAngle = 50;
private int mMaxZoom = -380;
private int mCoveflowCenter;
private boolean mAlphaMode = true;
private boolean mCircleMode = false;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
public boolean getCircleMode() {
return mCircleMode;
}
public void setCircleMode(boolean isCircle) {
mCircleMode = isCircle;
}
public boolean getAlphaMode() {
return mAlphaMode;
}
public void setAlphaMode(boolean isAlpha) {
mAlphaMode = isAlpha;
}
public int getMaxZoom() {
return mMaxZoom;
}
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ getPaddingLeft();
}
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth();
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle
: mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle);
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w
* Current width of this view.
* @param h
* Current height of this view.
* @param oldw
* Old width of this view.
* @param oldh
* Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
*
* @param imageView
* ImageView the ImageView whose bitmap we want to rotate
* @param t
* transformation
* @param rotationAngle
* the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t,
int rotationAngle) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();
final int imageHeight = child.getLayoutParams().height;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
// As the angle of the view gets less, zoom in
if (rotation <= mMaxRotationAngle) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
if (mCircleMode) {
if (rotation < 40)
mCamera.translate(0.0f, 155, 0.0f);
else
mCamera.translate(0.0f, (255 - rotation * 2.5f), 0.0f);
}
if (mAlphaMode) {
((ImageView) (child)).setAlpha((int) (255 - rotation * 2.5));
}
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
mCamera.restore();
}
}
这个就是CoverFlow类,说明几点
1. 成员函数
mCamera是用来做类3D效果处理,比如z轴方向上的平移,绕y轴的旋转等
mMaxRotationAngle是图片绕y轴最大旋转角度,也就是屏幕最边上那两张图片的旋转角度
mMaxZoom是图片在z轴平移的距离,视觉上看起来就是放大缩小的效果.
其他的变量都可以无视
2. 构造函数里面的setStaticTransformationsEnabled
protected void setStaticTransformationsEnabled (boolean enabled)
When this property is set to true, this ViewGroup supports static transformations on children; this causes getChildStaticTransformation(View, android.view.animation.Transformation) to be invoked when a child is drawn. Any subclass overriding getChildStaticTransformation(View, android.view.animation.Transformation) should set this property to true.
Parameters
enabled True to enable static transformations on children, false otherwise.
也就是说把这个属性设成true的时候每次viewGroup(看Gallery的源码就可以看到它是从ViewGroup间接继承过来的)在重新画它的child的时候都会促发getChildStaticTransformation这个函数,所以我们只需要在这个函数里面去加上旋转和放大的操作就可以了
其他的getter和setter函数都可以无视
view plaincopy to clipboardprint?
package com.hello;
import com.hello.R;
import com.myview.*;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoverFlow cf = new CoverFlow(this);
cf.setBackgroundResource(R.drawable.shape);
cf.setAdapter(new ImageAdapter(this));
ImageAdapter imageAdapter = new ImageAdapter(this);
cf.setAdapter(imageAdapter);
// cf.setAlphaMode(false);
// cf.setCircleMode(false);
// cf.setSelection(3, true);
cf.setAnimationDuration(1000);
setContentView(cf);
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private Integer[] mImageIds = {
R.drawable.a,
R.drawable.b,
R.drawable.d,
R.drawable.e,
R.drawable.c};
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ReflectionImage i = new ReflectionImage(mContext);
i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(96, 76));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
// Make sure we set anti-aliasing otherwise we get jaggies
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
}
public float getScale(boolean focused, int offset) {
return Math.max(0, 1.0f / (float) Math.pow(2, Math.abs(offset)));
}
}
}
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
是保证图片绕Y旋转了以后不会出现锯齿
CoverFlow--我也不知道为什么要叫这个名字,貌似是从iphone上继承过来的?
随便了,反正就是这个样子了
此控件的设计和实现思路和部分代码同样是剽窃某网站上的,因为时间太久了,找不到原文地址了..杯具,所以我才来做备份的.
此类是从Gallery继承过来的,用法自然也就和Gallery一样了
程序的背景是一个xml陪的渐变背景,具体可以参看另外一篇"渐变背景"的文章
图片的倒影效果是适用了ReflectionImage控件,具体可以参看上一篇博文.
先看代码吧,看了代码什么都明白了
view plaincopy to clipboardprint?
package com.myview;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Transformation;
import android.widget.Gallery;
import android.widget.ImageView;
public class CoverFlow extends Gallery {
private Camera mCamera = new Camera();
private int mMaxRotationAngle = 50;
private int mMaxZoom = -380;
private int mCoveflowCenter;
private boolean mAlphaMode = true;
private boolean mCircleMode = false;
public CoverFlow(Context context) {
super(context);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs) {
super(context, attrs);
this.setStaticTransformationsEnabled(true);
}
public CoverFlow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setStaticTransformationsEnabled(true);
}
public int getMaxRotationAngle() {
return mMaxRotationAngle;
}
public void setMaxRotationAngle(int maxRotationAngle) {
mMaxRotationAngle = maxRotationAngle;
}
public boolean getCircleMode() {
return mCircleMode;
}
public void setCircleMode(boolean isCircle) {
mCircleMode = isCircle;
}
public boolean getAlphaMode() {
return mAlphaMode;
}
public void setAlphaMode(boolean isAlpha) {
mAlphaMode = isAlpha;
}
public int getMaxZoom() {
return mMaxZoom;
}
public void setMaxZoom(int maxZoom) {
mMaxZoom = maxZoom;
}
private int getCenterOfCoverflow() {
return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
+ getPaddingLeft();
}
private static int getCenterOfView(View view) {
return view.getLeft() + view.getWidth() / 2;
}
protected boolean getChildStaticTransformation(View child, Transformation t) {
final int childCenter = getCenterOfView(child);
final int childWidth = child.getWidth();
int rotationAngle = 0;
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
if (childCenter == mCoveflowCenter) {
transformImageBitmap((ImageView) child, t, 0);
} else {
rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
if (Math.abs(rotationAngle) > mMaxRotationAngle) {
rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle
: mMaxRotationAngle;
}
transformImageBitmap((ImageView) child, t, rotationAngle);
}
return true;
}
/**
* This is called during layout when the size of this view has changed. If
* you were just added to the view hierarchy, you're called with the old
* values of 0.
*
* @param w
* Current width of this view.
* @param h
* Current height of this view.
* @param oldw
* Old width of this view.
* @param oldh
* Old height of this view.
*/
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCoveflowCenter = getCenterOfCoverflow();
super.onSizeChanged(w, h, oldw, oldh);
}
/**
* Transform the Image Bitmap by the Angle passed
*
* @param imageView
* ImageView the ImageView whose bitmap we want to rotate
* @param t
* transformation
* @param rotationAngle
* the Angle by which to rotate the Bitmap
*/
private void transformImageBitmap(ImageView child, Transformation t,
int rotationAngle) {
mCamera.save();
final Matrix imageMatrix = t.getMatrix();
final int imageHeight = child.getLayoutParams().height;
final int imageWidth = child.getLayoutParams().width;
final int rotation = Math.abs(rotationAngle);
mCamera.translate(0.0f, 0.0f, 100.0f);
// As the angle of the view gets less, zoom in
if (rotation <= mMaxRotationAngle) {
float zoomAmount = (float) (mMaxZoom + (rotation * 1.5));
mCamera.translate(0.0f, 0.0f, zoomAmount);
if (mCircleMode) {
if (rotation < 40)
mCamera.translate(0.0f, 155, 0.0f);
else
mCamera.translate(0.0f, (255 - rotation * 2.5f), 0.0f);
}
if (mAlphaMode) {
((ImageView) (child)).setAlpha((int) (255 - rotation * 2.5));
}
}
mCamera.rotateY(rotationAngle);
mCamera.getMatrix(imageMatrix);
imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
mCamera.restore();
}
}
这个就是CoverFlow类,说明几点
1. 成员函数
mCamera是用来做类3D效果处理,比如z轴方向上的平移,绕y轴的旋转等
mMaxRotationAngle是图片绕y轴最大旋转角度,也就是屏幕最边上那两张图片的旋转角度
mMaxZoom是图片在z轴平移的距离,视觉上看起来就是放大缩小的效果.
其他的变量都可以无视
2. 构造函数里面的setStaticTransformationsEnabled
protected void setStaticTransformationsEnabled (boolean enabled)
When this property is set to true, this ViewGroup supports static transformations on children; this causes getChildStaticTransformation(View, android.view.animation.Transformation) to be invoked when a child is drawn. Any subclass overriding getChildStaticTransformation(View, android.view.animation.Transformation) should set this property to true.
Parameters
enabled True to enable static transformations on children, false otherwise.
也就是说把这个属性设成true的时候每次viewGroup(看Gallery的源码就可以看到它是从ViewGroup间接继承过来的)在重新画它的child的时候都会促发getChildStaticTransformation这个函数,所以我们只需要在这个函数里面去加上旋转和放大的操作就可以了
其他的getter和setter函数都可以无视
view plaincopy to clipboardprint?
package com.hello;
import com.hello.R;
import com.myview.*;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
public class HelloAndroid extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoverFlow cf = new CoverFlow(this);
cf.setBackgroundResource(R.drawable.shape);
cf.setAdapter(new ImageAdapter(this));
ImageAdapter imageAdapter = new ImageAdapter(this);
cf.setAdapter(imageAdapter);
// cf.setAlphaMode(false);
// cf.setCircleMode(false);
// cf.setSelection(3, true);
cf.setAnimationDuration(1000);
setContentView(cf);
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private Integer[] mImageIds = {
R.drawable.a,
R.drawable.b,
R.drawable.d,
R.drawable.e,
R.drawable.c};
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ReflectionImage i = new ReflectionImage(mContext);
i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(96, 76));
i.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
// Make sure we set anti-aliasing otherwise we get jaggies
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
return i;
}
public float getScale(boolean focused, int offset) {
return Math.max(0, 1.0f / (float) Math.pow(2, Math.abs(offset)));
}
}
}
BitmapDrawable drawable = (BitmapDrawable) i.getDrawable();
drawable.setAntiAlias(true);
是保证图片绕Y旋转了以后不会出现锯齿
1 楼
zxxcos
2011-09-03
实现是实现了,可是写的太死,什么事件和Adapter都用不了,没多大的用处。
最新技术文章: