life02

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  197 随笔 :: 3 文章 :: 37 评论 :: 0 Trackbacks
http://apps.hi.baidu.com/share/detail/43342917
 

看到同事都用来“自动重播”软件来订火车票了,我也下载一个来看看,下载回来反编译一下看看,好像有广告的啊,有一个好像是Google的还是那个广告类在里面。索性自己弄一个好了,现在吸金软件很多啊,自己编译的才放心,呵呵!代码不多,又有反编译过来的代码作为才看,虽然反编译过来好像有的小地方不是很对,不过代码逻辑还大概看得出来的,然后自己看了下Android SDK的文档,Google了两下,大概可以弄个出来。就是那个鬼模拟器和Eclipse太好资源了。在我的机器上几乎跑不动了,浪费点时间。当然看Android SDK文档理解一下基本概念也花了不少时间。

        不过很悲剧啊,早上起来快9点了,然后我用软件一拨进去了,结果我搞错时间了,以为还没到我要买的日期就退了出来。等过一会再拨的时候票已经卖完了,有同事说是分时间段放票出来的,再试试看或者有
"软a卧"也买了。

 

来看程序吧:

        整个程序的代码逻辑很简单,就是先启动一个activity界面给用户输入,然后启动一个后台service不停的自动拨打相应的号码,直到拨通为止。Android下面相应的关键功能实现代码:

1. 拨打电话

通过Intent.ACTION_CALL 来请求系统的拨号Activity就可以了。在Android系统里面一个 Intent其实就相当于一条消息,然后系统就会自动根据的你的请求去找相应的功能模块比如说package里面的那个类来加载了。应用都事先注册了说我自己能够接受那些Intent的。拨打电话也有他的一个Intent 就是 Intent.ACTION_CALL ,其他的发送短信啊那些也有其他的,自己看下文档就知道了。

‍ 
private void PhoneCall()
 {
  
try {
   Thread.sleep(
2000);
  } 
catch (InterruptedException e) {
   
// TODO Auto-generated catch block
   e.printStackTrace();
  }
  mJustCall 
= false;
     Uri localUri 
= Uri.parse("tel:" + mPhoneNumber);
     Intent call 
= new Intent(Intent.ACTION_CALL, localUri);
     call.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);;
     startActivity(call);
     
//android.util.Log.v("TeleListener", "start to call"); 
     mDialedCount ++;
 }

注意这个功能要android.permission.CALL_PHONE的权限才能使用,所以要在AndroidManifest.xml 文件后面加上这句

<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>

安装程序的时候,有什么“需要收费的服务什么的”提示就是这条了。

 

2. 手机通话状态的查询  

     这个程序还有一个功能就是能够判断手机是不是正在拨号啊通话啊,才好控制自动重播的。这个Android有通知事件过来的,就像以前的“位置感应
"事件一样,只要你注册了这个通知事件,手机状态变化的时候就自动调用你Listener了。不过Android手机这个事件没法区分“拨号”和“通话”这两个状态的,这两者统一都是TelephonyManager.CALL_STATE_OFFHOOK状态,TelephonyManager.CALL_STATE_RINGING指的是外面拨进来的响铃吧。不过你看他系统里面其实是可以区分“拨号”和“通话”这两个状态的,打通了他就开始计时,界面也显示“正在通话”,只是它没有向外部应用开放这个接口而已。可以去看Android源码的packages/apps/Phone/src/com/android/phone/ 下面的InCallScreen.java  CallCard.java 等文件。鉴于我们的“xxxxx”,可以到http://www.netmite.com/android/mydroid/packages/apps/Phone/src/com/android/phone/  这里去看吧,网上好像有很多提供在线源码的

http:
//hi-android.info/src/com/android/phone/ 

 http:
//gitorious.org/0xdroid/packages_apps_phone/trees/5f6f01ecda4336dfb47108e67ff909a65f14b820/src/com/android/phone

另外监a听这个事件需要权限
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>

其实网上那些软件比如说“别人大个电话过来播放随机铃声”应该也是通过这个来做到的吧。

     TelephonyManager telephonyMgr 
= (TelephonyManager)getSystemService("phone");
     TeleListener teleListener 
= new TeleListener(this);
     telephonyMgr.listen(teleListener, 
0);

 

 
class TeleListener extends PhoneStateListener {
  
private AutoRedialService manager;
     
public TeleListener(AutoRedialService a)
     {
       
this.manager = a;
     }
  @Override
  
public void onCallStateChanged(int state, String incomingNumber) {
   
super.onCallStateChanged(state, incomingNumber);
   
switch (state) {
   
// 当处于待机状态中
   case TelephonyManager.CALL_STATE_IDLE: {
    manager.Log(
"IDLE");
    
//android.util.Log.v("TeleListener", "IDLE"); 
    if (manager.ShouldStop() || manager.LastCallSucceed()) {         
     manager.stopSelf();  
     
break;
    }            
    PhoneCall();    
    
break;
   }
    
// 当处于正在拨号出去,或者正在通话中
   case TelephonyManager.CALL_STATE_OFFHOOK: {
    manager.Log(
"OFFHOOK");
    
//android.util.Log.v("TeleListener", "OFFHOOK"); 
    mJustCall = true;
    
//Timer t = new Timer(); 
    break;
   }
    
// 外面拨进来,好没有接拨号状态中..
   case TelephonyManager.CALL_STATE_RINGING: {
    manager.Log(
"RINGING");
    
//android.util.Log.v("TeleListener", "RINGING"); 
    break;
   }
   
default:
    
break;
   }
  }

 

 

 
3"通一话一记一录"查询   (妈的这都是关键词啊,搞我半天发不上了)

上面说了不能区分
"接通"与“拨号”状态,所以我想到的办法只有拨完之后再去查看最近的通话记录了,如果最后一条记录的通话时间是大于0的,说明就拨通了,就不需要再重播了。这个用起来还可以,程序拨通就给我退出了。在Android里面这个信息需要向“content provider内a容提供者”去要,其实就是类似查询数据库,可以查、增、减之类的,自己的程序也可以提供这种接口供别人查询。注意这个也需要权a限

<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
 我一开始不知道,结果用那个蜗牛一样模拟器调试了半天才调试出来。

 
private boolean LastCallSucceed ()
 {
 
  
if ( mJustCall == false) {
        
return false;
  } 
 
  String[] projection 
= new String[] {
        Calls.NUMBER,
        Calls.DURATION
    };
 
  ContentResolver cr 
= getContentResolver();
  
final Cursor cur = cr.query(android.provider.CallLog.Calls.CONTENT_URI,
    projection,
    
null,
    
null,
    Calls.DEFAULT_SORT_ORDER);
   
if (cur.moveToFirst()) {
   
int duration = cur.getInt(1);
   
//上次通话时间
   if (duration > 0 )
   {  
    
//android.util.Log.v("TeleListener", "|"+ String.valueOf(duration) + "|");
    
//Log( "|"+ String.valueOf(duration) + "|");
    return true;
   }
      }
 
  
return false;
 }

 

4. Activity 和Service直接的通讯

在Android里面activity和service是分别属于两个不同的进程的,要两者交互还挺麻烦的,按照文档说法,要实现bindservice等大堆接口,弄起来挺麻烦的,我这一个这么小玩意就算了吧。偷看了一下反编译的出来的代码,里面启动service的时候,activity发送出去Intent消息携带多一点信息过去,然后在service里面可以读出来,但是service想传点数据回activity就不行了。这种简单的办法也够用了,还有朋友发现什么简单办法可以告诉我一下。

在 activity中

 
public void onClick(View v) {

     
switch (v.getId()) {

     
case R.id.Button_Start:
      Intent msg 
= new Intent(this, AutoRedialService.class);
         EditText pnumEdit 
= (EditText) findViewById(R.id.EditText_phoneNumber);
         EditText retryEdit 
= (EditText)findViewById(R.id.EditText_RetryCount);
         String phoneNumber 
=  pnumEdit.getText().toString();
         Integer i 
= Integer.decode(retryEdit.getText().toString());
      
int retryCount = i.intValue();
         msg.putExtra(
"RetryCount", retryCount);
      msg.putExtra(
"PhoneNumber",phoneNumber);
      
//retryEdit.setText(String.valueOf(retryCount-1));
      startService(msg);

 

然后在service启动的时候可以读出来

 
public void onStart(Intent intent, int startID) {
  
super.onStart(intent, startID);
  mRetryCount 
= intent.getIntExtra("RetryCount"0);
  String tmp  
= intent.getStringExtra("PhoneNumber");
  
if (tmp != null)
  {
   mPhoneNumber 
= tmp; 
  }

 

写完之后,我发程序发到我的手机上去试了一下,好像还行啊,有时需要拨几十次才能打通电话,呵呵,真是省去不少功夫了。有点小问题,有时想停止的时候,不那么好控制,我把重拨间隔定为2秒了,要操作的很快才行啊。还有就是那个“拨号”“通话”两种状态的变化,感觉可以去读取拨号程序的界面来判断,如果能够找到那个activity的实例,然后就好办了再findViewById就可以读出上面控件的状态了。不过android好像很难获取别的activity的实例,即使这个应用是自己的同一个application启动起来的。看文档好像通过android 测试接口Instrumentation  下面的的activitymonitor什么也许可以获取的 到其他activity的实例,不过我没有去试,这个需要建一个“android测试”项目然后从那开始吧。

 

完整的代码

================AutoRedialService。java================================

package widebright.AutoRedial;

import java.util.Timer;

import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.CallLog.Calls;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class AutoRedialService extends Service {
    
private int mRetryCount = 0;
    
private int mDialedCount = 0;
 
private String mPhoneNumber= "10086";
 
private String mDebugLog = "";
 
private boolean mJustCall = false;
 
    
public void Log (String text){
     
//mDebugLog += text;
    }       
   
 @Override
 
public IBinder onBind(Intent msg) {
  
// 使用 bind的办法,可以方便的在service和
  
//activity两个不同的进程直接交互,不过看代码很多啊
 
  
return null;
 
 }
 
 
private void PhoneCall()
 {
  
try {
   Thread.sleep(
2000);
  } 
catch (InterruptedException e) {
   
// TODO Auto-generated catch block
   e.printStackTrace();
  }
  mJustCall 
= false;
     Uri localUri 
= Uri.parse("tel:" + mPhoneNumber);
     Intent call 
= new Intent(Intent.ACTION_CALL, localUri);
     call.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);;
     startActivity(call);
     
//android.util.Log.v("TeleListener", "start to call");
     mDialedCount ++;
 }
 
 
private  boolean ShouldStop()
 {
  
return  mDialedCount > mRetryCount;
 }
 
 
private boolean LastCallSucceed ()
 {
 
  
if ( mJustCall == false) {
        
return false;
  } 
 
  String[] projection 
= new String[] {
        Calls.NUMBER,
        Calls.DURATION
    };
 
  ContentResolver cr 
= getContentResolver();
  
final Cursor cur = cr.query(android.provider.CallLog.Calls.CONTENT_URI,
    projection,
    
null,
    
null,
    Calls.DEFAULT_SORT_ORDER);
   
if (cur.moveToFirst()) {
   
int duration = cur.getInt(1);
   
//上次通话时间
   if (duration > 0 )
   {  
    
//android.util.Log.v("TeleListener", "|"+ String.valueOf(duration) + "|");
    
//Log( "|"+ String.valueOf(duration) + "|");
    return true;
   }
      }
 
  
return false;
 }
   
public void onCreate()
   {
     
super.onCreate();
   }

 
public void onStart(Intent intent, int startID) {
  
super.onStart(intent, startID);
  
//android.util.Log.v("TeleListener", "starting haha");
  
// 获取电话管理的一个类实例
  mRetryCount = intent.getIntExtra("RetryCount"0);
  String tmp  
= intent.getStringExtra("PhoneNumber");
  
if (tmp != null)
  {
   mPhoneNumber 
= tmp; 
  }
 
  TelephonyManager telephonyMgr 
= (TelephonyManager) this
    .getSystemService(Context.TELEPHONY_SERVICE);

  
// 建立一个监听器来实时监听电话的通话状态
  telephonyMgr.listen(new TeleListener(this),
    PhoneStateListener.LISTEN_CALL_STATE);

 
  mDialedCount 
= 0;
  
//PhoneCall();

 }

 
public void onDestroy()
 {
     TelephonyManager telephonyMgr 
= (TelephonyManager)getSystemService("phone");
     TeleListener teleListener 
= new TeleListener(this);
     telephonyMgr.listen(teleListener, 
0);
     mDialedCount 
= 0;
     mRetryCount 
= 0;
     mPhoneNumber
= "10086";
     
super.onDestroy();
 }
  
 
class TeleListener extends PhoneStateListener {
  
private AutoRedialService manager;
     
public TeleListener(AutoRedialService a)
     {
       
this.manager = a;
     }
  @Override
  
public void onCallStateChanged(int state, String incomingNumber) {
   
super.onCallStateChanged(state, incomingNumber);
   
switch (state) {
   
// 当处于待机状态中
   case TelephonyManager.CALL_STATE_IDLE: {
    manager.Log(
"IDLE");
    
//android.util.Log.v("TeleListener", "IDLE");
    if (manager.ShouldStop() || manager.LastCallSucceed()) {        
     manager.stopSelf(); 
     
break;
    }           
    PhoneCall();   
    
break;
   }
    
// 当处于正在拨号出去,或者正在通话中
   case TelephonyManager.CALL_STATE_OFFHOOK: {
    manager.Log(
"OFFHOOK");
    
//android.util.Log.v("TeleListener", "OFFHOOK");
    mJustCall = true;
    
//Timer t = new Timer(); 
    break;
   }
    
// 外面拨进来,好没有接拨号状态中..
   case TelephonyManager.CALL_STATE_RINGING: {
    manager.Log(
"RINGING");
    
//android.util.Log.v("TeleListener", "RINGING");
    break;
   }
   
default:
    
break;
   }
  }

 }
 
 
}


===============================main.java================================

package widebright.AutoRedial;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class main extends Activity implements OnClickListener {
 Button buttonStart, buttonStop;
    
/** Called when the activity is first created. */
    @Override
    
public void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
//EditText pnumEdit = (EditText) findViewById(R.id.EditText_phoneNumber);
        
//EditText retryEdit = (EditText)findViewById(R.id.EditText_RetryCount);
     
//retryEdit.setText("10");
      
       buttonStart 
= (Button) findViewById(R.id.Button_Start);
       buttonStop 
= (Button) findViewById(R.id.Button_Stop);
       buttonStart.setOnClickListener(
this);
       buttonStop.setOnClickListener(
this);
    }
    @Override
 
public void onClick(View v) {

     
switch (v.getId()) {

     
case R.id.Button_Start:
      Intent msg 
= new Intent(this, AutoRedialService.class);
         EditText pnumEdit 
= (EditText) findViewById(R.id.EditText_phoneNumber);
         EditText retryEdit 
= (EditText)findViewById(R.id.EditText_RetryCount);
         String phoneNumber 
=  pnumEdit.getText().toString();
         Integer i 
= Integer.decode(retryEdit.getText().toString());
      
int retryCount = i.intValue();
         msg.putExtra(
"RetryCount", retryCount);
      msg.putExtra(
"PhoneNumber",phoneNumber);
      
//retryEdit.setText(String.valueOf(retryCount-1));
      startService(msg);        
      
       
break;
     
case R.id.Button_Stop:
          stopService(
new Intent(this, AutoRedialService.class));
             
break;
         
default:
          
break;
     }
 }
   
   
   
}


=====================AutoRedialManifest.xm===========================================

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      
package="widebright.AutoRedial"
      android:versionCode
="1"
      android:versionName
="1.0">
    
<application android:icon="@drawable/icon" android:label="@string/app_name">
  
<activity android:name=".main" android:icon="@drawable/icon"
   android:label
="@string/app_name">
   
<intent-filter>
    
<action android:name="android.intent.action.MAIN" />
    
<category android:name="android.intent.category.LAUNCHER" />
   
</intent-filter>
  
</activity>

  
<service android:icon="@drawable/icon" android:label="@string/app_name"
   android:name
=".AutoRedialService">
  
</service>

    
</application>
    
<uses-sdk android:minSdkVersion="7" />
 
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
 
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
 
<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
 

</manifest>

 

 

============================layout/main.xml=============================

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation
="vertical" android:layout_width="fill_parent"
 android:layout_height
="fill_parent" android:layout_gravity="right">
 
<TextView android:layout_width="fill_parent"
  android:layout_height
="wrap_content"
  android:text
="@string/hello" />

 
<TextView android:text="@string/number"
           android:id
="@+id/TextView01"
        android:layout_width
="fill_parent"
        android:layout_height
="wrap_content">
 
</TextView>
 
<EditText android:layout_width="fill_parent"
        android:singleLine
="true"
        android:layout_height
="wrap_content"
        android:text
="10086"
           android:phoneNumber
="true"
           android:id
="@+id/EditText_phoneNumber">
           
</EditText>
 
<TextView android:text="@string/times"
           android:id
="@+id/TextView02"
        android:layout_width
="wrap_content"
        android:layout_height
="wrap_content">
        
</TextView>
 
<EditText android:layout_width="fill_parent"
        android:layout_height
="wrap_content"
        android:text
="100"
        android:id
="@+id/EditText_RetryCount"
        android:numeric
="integer"
        android:singleLine
="true"
        android:inputType
="number">
        
</EditText>


<LinearLayout android:id="@+id/LinearLayout01" android:layout_width="fill_parent" android:layout_height="wrap_content">
<Button android:text="@string/start"
        android:id
="@+id/Button_Start"
        android:layout_height
="wrap_content" android:layout_width="wrap_content" android:layout_gravity="left">
        
</Button>
<Button android:text="@string/stop"
        android:id
="@+id/Button_Stop"
        android:layout_height
="wrap_content" android:layout_gravity="right" android:layout_width="wrap_content">
        
</Button>
</LinearLayout>

</LinearLayout>


==========================================

 

 

 

再发两个网上找到的小实例,没有测试过

1. 模拟键盘按键

  
final IWindowManager windowManager = IWindowManager.Stub
               .asInterface(ServiceManager.getService(
"window"));

windowManager.injectchangerchangerKeyEvent(kEvent,
true);

2. 拦截拨打电话号码操作

1.第一步,写一个Receiver继承自BroadcastReceiver
public class PhoneStatReceiver extends BroadcastReceiver{
       
        
private static final String TAG = "PhoneStatReceiver";
       
//        private static MyPhoneStateListener phoneListener = new MyPhoneStateListener();
       
        
private static boolean incomingFlag = false;
       
        
private static String incoming_number = null;

        @Override
        
public void onReceive(Context context, Intent intent) {
                
//如果是拨打电话
                if(intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){                       
                        incomingFlag 
= false;
                        String phoneNumber 
= intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);       
                        Log.i(TAG, 
"call OUT:"+phoneNumber);                       
                }
else{                       
                        
//如果是来电
                        TelephonyManager tm =
                            (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);                       
                       
                        
switch (tm.getCallState()) {
                        
case TelephonyManager.CALL_STATE_RINGING:
                                incomingFlag 
= true;//标识当前是来电
                                incoming_number = intent.getStringExtra("incoming_number");
                                Log.i(TAG, 
"RINGING :"+ incoming_number);
                                
break;
                        
case TelephonyManager.CALL_STATE_OFFHOOK:                               
                                
if(incomingFlag){
                                        Log.i(TAG, 
"incoming ACCEPT :"+ incoming_number);
                                }
                                
break;
                       
                        
case TelephonyManager.CALL_STATE_IDLE:                               
                                
if(incomingFlag){
                                        Log.i(TAG, 
"incoming IDLE");                              
                                }
                                
break;
                        }
                }
        }
}

第二步:在AndroidManifest.xml,配置写好的Receiver,并拦截相应的BroadCastAction,
另外注意加上相应的权限。
<receiver android:name=".filter.PhoneStatReceiver"> 
            
<intent-filter>
                 
<action android:name="android.intent.action.PHONE_STATE"/>          
                 
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            
</intent-filter>
</receiver>

另外有拨打紧急号码权限

   
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
posted on 2011-12-15 22:40 life02 阅读(1527) 评论(0)  编辑 收藏 引用 所属分类: Android开发

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理