好久没写博客了,实在是太忙了,又鉴于鄙人天生爱睡觉。幸运的是,上周终于把自己的第一个Apk写好了,,接下来,就写个小系列来记录一下吧。不过,在此之前,先把这篇黑科技写起来(研究了小段时间,确实好玩)。
原理思路:1、利用alarm机制来定时
2、利用广播,启动新的Activity拨打电话
3、利用反射机制,取得手机状态,接通后一定时间挂断
接下来上详细的代码块:
一、点设置之后会有一个timepickDialog弹出,用来设置AlarmClock的启动时间
btnSettting.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
c.setTimeInMillis(System.currentTimeMillis());
new TimePickerDialog(MainActivity.this,
new OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view,
int hourOfDay, int minute) {
clockHour = hourOfDay;
clockMinute = minute;
phoneTime.setText(clockHour + " : "
+ clockMinute);
}
}, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE),
true).show();
}
});
分析:还是很好理解的,clockHour和clockMinute分别用来记录拨打的小时、分钟
二、通过PendingIntent结合Alarm来定时启动广播
btnStart.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
intent = new Intent(MainActivity.this, PhoneBroadcast.class);
intent.putExtra("phoneNumber", phoneNumber.getText().toString());
pIntent = PendingIntent.getBroadcast(MainActivity.this, 0,
intent, 0);
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, clockHour);
calendar.set(Calendar.MINUTE, clockMinute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), DURATION_TIME, pIntent);
Toast.makeText(MainActivity.this,
"已启动,时间是" + clockHour + " : " + clockMinute,
Toast.LENGTH_SHORT).show();
}
});
分析:这边也比较好理解,主要是用到AlarmManager类来管理闹钟,取得实例后,setRepeating方法来设置闹钟。四个参数分别为,闹钟模式(4种,可百度),闹钟启动时间,重复间隔,PendingIntent。
自定义的BroadCast
public class PhoneBroadcast extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Intent intent2=new Intent(context, PhoneActivity.class);
intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent2.putExtra("phoneNumber",intent.getStringExtra("phoneNumber") );
context.startActivity(intent2);
}
}
用来启动打电话的Activity
第三步:难点
这边有点复杂的,老实说我并没有完全弄懂
新建包com.android.internal.telephony,在其中新建一个File,后缀为aidl(它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口),内容如下
package com.android.internal.telephony;
interface ITelephony{
boolean endCall();
void answerRingingCall();
}
这之后会在gen目标下生成对应java文件(你无法修改,但是可以用ITelephoney接口了)
回到打电话的Activity,先上代码。
package com.dota.example.autophoneapp;
import java.lang.reflect.Method;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.view.WindowManager;
import com.android.internal.telephony.ITelephony;
public class PhoneActivity extends Activity {
private ITelephony itelephony;
private TelephonyManager manager;
public Handler mHandler;
private Thread mThread;
private static final int END_CALL_REQUEST = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
Intent intent = getIntent();
String phoneNumber = intent.getStringExtra("phoneNumber");
Intent intentPhone = new Intent(Intent.ACTION_CALL);
intentPhone.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intentPhone);
phoner();
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case END_CALL_REQUEST:
try {
itelephony.endCall();
finish();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
default:
break;
}
}
};
mThread=new NewThread();
manager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
try {
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
mThread.start();
break;
default:
break;
}
} catch (Exception e) {
// TODO: handle exception
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
public void phoner() {
manager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
Class class1 = TelephonyManager.class;
Method getITelephonyMethod = null;
try {
getITelephonyMethod = class1.getDeclaredMethod("getITelephony",
(Class[]) null);
getITelephonyMethod.setAccessible(true);
itelephony = (ITelephony) getITelephonyMethod.invoke(manager,
(Object[]) null);
} catch (Exception e) {
// TODO: handle exception
}
}
class NewThread extends Thread implements Runnable {
@Override
public void run() {
super.run();
try {
Thread.sleep(1000*20);
Message message = new Message();
message.what = END_CALL_REQUEST;
mHandler.sendMessage(message);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
来,慢慢分析。先从pnoner()方法入手。这里用到反射的方法,得到TelephonyManager类中的getITelephony方法,将其设置为可操作。
manager.listen(new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
try {
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
mThread.start();
break;
default:
break;
}
} catch (Exception e) {
// TODO: handle exception
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
三种状态:RINGING(振铃)、OFFHOOK(摘机=接通)、IDLE(空闲)
这边用到Hander Message(异步线程处理的原理),接受到状态后,启动线程,sleep20秒,执行
itelephony.endCall();
即:挂断
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
这段是用来打电话的:打电话是ACTION_CALL,设置拨打的号码为Uri.parse("tel:10086");
String phoneNumber = intent.getStringExtra("phoneNumber");
Intent intentPhone = new Intent(Intent.ACTION_CALL);
intentPhone.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intentPhone);
还有一些不重要的就不写了,代码里可以看到,还是很有意思的(最终我被朋友拉黑了)
源代码的百度云链接:http://pan.baidu.com/s/1i3kWH85