/*Google AdSense自動広告*/

2014年7月23日水曜日

00 Plus Call ソースコード公開

私のスマホに入っているSIMはデータ+電話のMVNOなので、そのままだと電話代が高くなります。そこでG-Callに加入して、電話代を半額にしたのですが、ただ発信時に0063を頭に付けるだけなのにG-Callのアプリ(重い)を入れたり、Prefixerという英語のアプリでせっせと設定したりするのが面倒になって、自分でアプリを作りました。

やりたいことはシンプルに、緊急電話やフリーダイヤル以外の発信時に必ず0063を頭につけるということです。

BroadcastReceiverでoutgoing callをキャッチし、プリフィックスを付加して新たなoutgoing callを発する動作をします。
下記の記事などを参考にしました。
How do you receive outgoing call in broadcastreceiver



mainActivity.java
PreferenceActivityで作ろうとしたのですが、Spinnerのように現状で選択している項目を表示させることが面倒くさそうだったので、普通のActivityです。


public class mainActivity extends Activity{
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  final Switch enableSwitch = (Switch)findViewById(R.id.enableSwitch);
  final Spinner selectSpinner = (Spinner)findViewById(R.id.selectSpinner);
  final RelativeLayout editBox = (RelativeLayout)findViewById(R.id.editBox);
  final Button editButton = (Button)findViewById(R.id.editButton);
  
  setInitialPreferences();
  
  PreferenceUtil prefUtil = new PreferenceUtil(this);
  
  enableSwitch.setChecked(prefUtil.GetBoolean("enable"));
  enableSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
   @Override
   public void onCheckedChanged(CompoundButton arg0, boolean checked) {
    PreferenceUtil prefUtil = new PreferenceUtil(mainActivity.this);
    prefUtil.SetBoolean("enable", checked);
   }
  });
  
  selectSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
   @Override
   public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    PreferenceUtil prefUtil = new PreferenceUtil(mainActivity.this);
    String selectedItem = parent.getSelectedItem().toString();
    prefUtil.SetInt("selectedPosition", position);
    
    if (selectedItem.equals(getString(R.string.select_item_etc))) {
     editBox.setVisibility(View.VISIBLE);
     prefUtil.SetString("prefix", prefUtil.GetString("inputPrefix"));
    } else {
     editBox.setVisibility(View.GONE);
     if (selectedItem.equals(getString(R.string.select_item_gcall))) {
      prefUtil.SetString("prefix", getString(R.string.number_gcall));
     } else if (selectedItem.equals(getString(R.string.select_item_rakuten))) {
      prefUtil.SetString("prefix", getString(R.string.number_rakuten));
     }
    }
   }

   @Override
   public void onNothingSelected(AdapterView<?> parent) {}   
  });
  selectSpinner.setSelection(prefUtil.GetInt("selectPotision"));
  
  editButton.setText(getString(R.string.edit_button) + " : " + prefUtil.GetString("inputPrefix"));
  editButton.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    final EditText editText = new EditText(mainActivity.this);
    PreferenceUtil prefUtil = new PreferenceUtil(mainActivity.this);
        
    editText.setText(prefUtil.GetString("prefix"));
    editText.setInputType(InputType.TYPE_CLASS_NUMBER);
    
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(mainActivity.this);
    alertDialog.setTitle(getString(R.string.edit_button));
    alertDialog.setView(editText);
    alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener(){
     public void onClick(DialogInterface dialog, int which) {
      String setText = editText.getText().toString();
      PreferenceUtil prefUtil = new PreferenceUtil(mainActivity.this);
      prefUtil.SetString("inputPrefix", setText);
      prefUtil.SetString("prefix", setText);
      editButton.setText(getString(R.string.edit_button) + " : " + setText);
     }});  
    alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){  
     public void onClick(DialogInterface dialog, int which) {  

     }});  
    alertDialog.show();  
   }
  });  
 }
 
 private void setInitialPreferences() {
  PreferenceUtil prefUtil = new PreferenceUtil(this);
  if (!prefUtil.GetBoolean("IsSetDefault")) {
   prefUtil.SetBoolean("enable", false);
   prefUtil.SetInt("selectedPosition", 0);
   prefUtil.SetString("prefix", getString(R.string.number_gcall));
   prefUtil.SetString("inputPrefix", "");
   prefUtil.SetBoolean("dialog", false);
   
   prefUtil.SetBoolean("IsSetDefault", true);
  }
 }
}


CallReceiver.java
Callを処理するBroadcastReceiverです。注意する点は、新しい call intentを発するともう一度このReceiverを通ること。うまくスルーしないと無限ループに陥ります。私の場合は、緊急電話やフリーダイヤルをスルーすることに加え、すでにプレフィックスが付加されている場合もスルーすることで対策を行いました。

public class CallReceiver extends BroadcastReceiver {
 @Override 
 public void onReceive(Context context, Intent intent) {
  if(intent.getAction().equalsIgnoreCase(Intent.ACTION_NEW_OUTGOING_CALL)) { 
   
   PreferenceUtil prefUtil = new PreferenceUtil(context);
   
   if (prefUtil.GetBoolean("enable")) {
    
    String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); 
    String prefix = prefUtil.GetString("prefix");

    if (isExcludeNumber(phoneNumber, prefix)) { 
     // ignore this process
    } else {
     // cut the dialed call and redial the new call.
     setResultData(null);

     Uri uri = Uri.fromParts("tel", prefix + phoneNumber, null);
     Intent callIntent = new Intent(Intent.ACTION_CALL, uri);
     callIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

     context.startActivity(callIntent);
    }
   }
  }
 }
 
 // free call, emergency call, prefix added
 private boolean isExcludeNumber(String phoneNumber, String prefix) {
  if (!prefix.isEmpty()) {
   if (phoneNumber.length() == 3 && phoneNumber.substring(0, 1).equals("1")) {
    return true;
   } else {
    String[] specialNumbers = {"0120", "0800", "0070", "0077", "0088", "0570", "0063", "003768", prefix};
    for (String specialNumber:specialNumbers) {
     if (phoneNumber.length() >= specialNumber.length()) {
      if (phoneNumber.substring(0, specialNumber.length()).equals(specialNumber)) {
       return true;
      }
     }
    }
   }
  }
  
  return false;
 }
}


PreferenceUtil.java
Preferenceを簡単に使うためのclassです。直接書く場合は必要ありません。

public class PreferenceUtil {
 private SharedPreferences sp;
 
 protected PreferenceUtil(Context context) {
  sp = context.getSharedPreferences("alarm", Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
 }
  
 public void SetString(String strKey, String strVal) {
  SharedPreferences.Editor editor = sp.edit();
  editor.putString(strKey, strVal);
  editor.commit();
 }
 
 public String GetString(String strKey) {
  return sp.getString(strKey, "");
 }
 
 public void SetInt(String strKey, int intVal) {
  SharedPreferences.Editor editor = sp.edit();
  editor.putInt(strKey, intVal);
  editor.commit();
 }
 
 public int GetInt(String strKey) {
  return sp.getInt(strKey, -1);
 }
  
 public void SetBoolean(String strKey, Boolean booleanVal) {
  SharedPreferences.Editor editor = sp.edit();
  editor.putBoolean(strKey, booleanVal);
  editor.commit();
 }
 
 public Boolean GetBoolean(String strKey) {
  return sp.getBoolean(strKey, false);
 }
 
 public void SetFloat(String strKey, Float FloatVal) {
  SharedPreferences.Editor editor = sp.edit();
  editor.putFloat(strKey, FloatVal);
  editor.commit();
 }
 
 public Float GetFloat(String strKey) {
  return sp.getFloat(strKey, 0.0f);
 } 
}


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >


        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="@dimen/prefs_height"
            android:layout_marginBottom="@dimen/prefs_bottom"
            android:layout_marginLeft="@dimen/prefs_left"
            android:layout_marginRight="@dimen/prefs_right" >

            <TextView
                android:id="@+id/textView1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:text="@string/enable"
                android:textAppearance="?android:attr/textAppearanceMedium" />

            <Switch
                android:id="@+id/enableSwitch"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true" />

        </RelativeLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="@dimen/prefs_height"
            android:layout_marginBottom="@dimen/prefs_bottom"
            android:layout_marginLeft="@dimen/prefs_left"
            android:layout_marginRight="@dimen/prefs_right" >

            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:text="@string/select"
                android:textAppearance="?android:attr/textAppearanceMedium" />

            <Spinner
                android:id="@+id/selectSpinner"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:entries="@array/select_list" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/editBox"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/prefs_height"
            android:layout_marginBottom="@dimen/prefs_bottom"
            android:layout_marginLeft="@dimen/prefs_left"
            android:layout_marginRight="@dimen/prefs_right" >

            <Button
                android:id="@+id/editButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:text="@string/edit_button" />
            
        </RelativeLayout>

</LinearLayout>


AndroidManifest.xml
call intentを発するためのpermissionが必要です。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mikotomoti.zeropluscall"
    android:versionCode="1"
    android:versionName="1.0.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="14" />

    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> 
    <uses-permission android:name="android.permission.CALL_PHONE" /> 
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        
        <activity
        android:name="com.mikotomoti.zeropluscall.mainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
     </activity>
     
     <receiver android:name="com.mikotomoti.zeropluscall.CallReceiver"> 
   <intent-filter android:priority="1"> 
    <action 
     android:name="android.intent.action.NEW_OUTGOING_CALL"></action> 
   </intent-filter> 
  </receiver> 
         
     </application>   

</manifest>

strings.xml
SpinnerにAdapterを介してアイテムを投入するのは少しコードが煩雑になります。アイテムが固定値の場合、string-arrayをレイアウトで入れてしまうほうが簡単です。

<resources>
    <string name="app_name">00 Plus Call</string>
 
    <string name="title_setting">設定</string>
 <string name="enable">有効</string>
 <string name="select">通話会社の選択</string>
 <string name="edit_button">番号を入力</string>
 <string name="dialog">確認画面の表示</string>
 
 <string-array name="select_list">
        <item>G-Call</item>
        <item>楽天でんわ</item>
        <item>その他</item>
    </string-array>
    <string name="select_item_gcall">G-Call</string> <!-- 判定に使用するので上のリストと同じにすること -->
    <string name="select_item_rakuten">楽天でんわ</string> <!-- 判定に使用するので上のリストと同じにすること -->
    <string name="select_item_etc">その他</string> <!-- 判定に使用するので上のリストと同じにすること -->

 <string name="number_gcall">0063</string>
 <string name="number_rakuten">003768</string>
    
</resources>


0 件のコメント:

コメントを投稿