@blog.justoneplanet.info

日々勉強

iOSのAddressBookを操作する

■グループの検索

CFArrayRef groups = ABAddressBookCopyArrayOfAllGroups(addressBook);
for (int i = 0; i < CFArrayGetCount(groups); i++) {
    ABRecordRef group = CFArrayGetValueAtIndex(groups, i);
    CFStringRef registerdGroupName = ABRecordCopyValue(group, kABGroupNameProperty);
    if (CFStringFind(registerdGroupName, aCFgroupName, kCFCompareCaseInsensitive).location != kCFNotFound) {
        isRegistered = true;
        registerdId = ABRecordGetRecordID(group);
    }
    CFRelease(registerdGroupName);
}
CFRelease(groups);

■グループの作成

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
ABAddressBookRef addressBook = ABAddressBookCreate();
bool b1 = false;
bool b2 = false;
bool b3 = false;
ABRecordRef group = ABGroupCreate();
NSString *groupName = NSLocalizedString(@"APP_NAME", @"グループ名");
CFStringRef aCFgroupName = (__bridge CFStringRef)groupName;
b1 = ABRecordSetValue(group, kABGroupNameProperty, aCFgroupName, nil);
            
if (b1) b2 = ABAddressBookAddRecord(addressBook, group, nil);
if (b2) b3 = ABAddressBookSave(addressBook, nil);
if (b3) {
    [ud setInteger:ABRecordGetRecordID(group) forKey:@"key_ab_group_id"];
    [ud synchronize];
}
CFRelease(group);
CFRelease(addressBook);

■グループの削除

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
ABAddressBookRef addressBook = ABAddressBookCreate();
bool b1 = false;
bool b2 = false;
ABRecordRef group = ABAddressBookGetGroupWithRecordID(addressBook, [ud integerForKey:@"key_ab_group_id"]);
b1 = ABAddressBookRemoveRecord(addressBook, group, nil);
if (b1) b2 = ABAddressBookSave(addressBook, nil);
if (b2) {
    [ud setInteger:0 forKey:@"key_ab_group_id"];
    [ud synchronize];
}
CFRelease(addressBook);

■グループのメンバ取得と削除

CFArrayRef people = ABGroupCopyArrayOfAllMembers(group);
for (int i = 0; i < CFArrayGetCount(people); i++) {
    ABRecordRef person = CFArrayGetValueAtIndex(people, i);
    result1 = ABGroupRemoveMember(group, person, nil);// グループから削除されるだけで電話帳には残る
    result2 = ABAddressBookRemoveRecord(addressBook, person, nil);// 電話帳から削除される
}

■グループへのメンバ追加

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];

ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef group = ABAddressBookGetGroupWithRecordID(addressBook, [ud integerForKey:@"key_ab_group_id"]);
ABRecordRef person = ABPersonCreate();

CFStringRef aCFfirst       = (CFStringRef)first;
CFStringRef aCFsecond      = (CFStringRef)second;
CFStringRef aCFfirstYomi   = (CFStringRef)firstYomi;
CFStringRef aCFsecondYomi  = (CFStringRef)secondYomi;
ABRecordSetValue(person, kABPersonFirstNameProperty, aCFfirst, nil);
ABRecordSetValue(person, kABPersonFirstNamePhoneticProperty, aCFfirstYomi, nil);
ABRecordSetValue(person, kABPersonLastNameProperty, aCFsecond, nil);
ABRecordSetValue(person, kABPersonLastNamePhoneticProperty, aCFsecondYomi, nil);

ABAddressBookAddRecord(addressBook, person, &error);
ABAddressBookSave(addressBook, &error);

if ([ud integerForKey:@"key_ab_group_id"] != 0) {
    ABGroupAddMember(group, person, &error);
}

ABAddressBookSave(addressBook, nil);
CFRelease(person);
CFRelease(addressBook);

参考

iOSで本体の設定画面に設定項目を追加する

本体の設定画面にアプリの設定画面が出るアレであるが、Settings Bundleで検索すると色々出てくる。

■実装

NEW FILE … > Resource > Settings Bundleを追加する。この段階でシミュレータを立ち上げれば設定画面にアプリの設定項目が追加されている。

Root.plist

初期状態は以下のようになっている。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PreferenceSpecifiers</key>
    <array>
        <dict>
            <key>Title</key>
            <string>Group</string>
            <key>Type</key>
            <string>PSGroupSpecifier</string>
        </dict>
        <dict>
            <key>AutocapitalizationType</key>
            <string>None</string>
            <key>AutocorrectionType</key>
            <string>No</string>
            <key>DefaultValue</key>
            <string></string>
            <key>IsSecure</key>
            <false/>
            <key>Key</key>
            <string>name_preference</string>
            <key>KeyboardType</key>
            <string>Alphabet</string>
            <key>Title</key>
            <string>Name</string>
            <key>Type</key>
            <string>PSTextFieldSpecifier</string>
        </dict>
        <dict>
            <key>DefaultValue</key>
            <true/>
            <key>Key</key>
            <string>enabled_preference</string>
            <key>Title</key>
            <string>Enabled</string>
            <key>Type</key>
            <string>PSToggleSwitchSpecifier</string>
        </dict>
        <dict>
            <key>DefaultValue</key>
            <real>0.5</real>
            <key>Key</key>
            <string>slider_preference</string>
            <key>MaximumValue</key>
            <integer>1</integer>
            <key>MaximumValueImage</key>
            <string></string>
            <key>MinimumValue</key>
            <integer>0</integer>
            <key>MinimumValueImage</key>
            <string></string>
            <key>Type</key>
            <string>PSSliderSpecifier</string>
        </dict>
    </array>
    <key>StringsTable</key>
    <string>Root</string>
</dict>
</plist>

■データの読み出し

以下のコードで設定値を呼び出すことが出来る。

NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSLog(@"%@", [ud objectForKey:@"name_preference"]);
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSLog(@"%@", [ud objectForKey:@"enabled_preference"]);
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSLog(@"%@", [ud objectForKey:@"slider_preference"]);

一番難しいのは何で検索すればこの画面の作り方に辿り着けるのかというところである。

AndroidでGCMを使ってみる

■クライアント側の実装

C2DMと対して変わらない。sender IDがメールアドレスでなくなったのは嬉しい。

gcm.jar

SDK/extras/google/の中からgcm.jarをlibsディレクトリの中にコピーする。プロジェクトをrefreshすれば現行のADTは勝手に組み込む。

manifest.xml

Android 2.2以降でないとダメなので以下のようにminSdkVersionを8とする。

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

permissionを追加する。

<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

アプリケーションがGCMを受け取るために以下の記述を付加する。

<permission
    android:name="org.sample.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="org.sample.permission.C2D_MESSAGE" />

receiverを追加する。

<receiver
    android:name="com.google.android.gcm.GCMBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="org.sample" />
    </intent-filter>
</receiver>

GCMIntentService

GCMBaseIntentServiceを継承したクラスを作る。

public class GCMIntentService extends GCMBaseIntentService {
    /**
     * 登録トークンを受け取った時に実行される。{@inheritDoc}
     */
    @Override
    public void onRegistered(Context context, String registration) {
        if (BuildConfig.DEBUG) Log.d("onRegistered", "registration id:" + registration);
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        Editor editor = preferences.edit();
        editor.putString(Constants.KEY_C2DM_REGISTRATION, registration);
        editor.commit();
    }
    /**
     * デバイスが登録解除した時に実行される。{@inheritDoc}
     */
    @Override
    protected void onUnregistered(Context arg0, String arg1) {
    }
    /**
     * クラウドメッセージが受信した時に実行される。{@inheritDoc}
     */
    @Override
    protected void onMessage(Context context, Intent intent) {
        if (BuildConfig.DEBUG) Log.d("onMessage", "onMessage:" + intent.getExtras().toString());
        setNotification(context);
    }
    /**
     * 登録エラーの時に実行される。
     */
    @Override
    public void onError(Context context, String errorId) {
        if (BuildConfig.DEBUG) Log.d("onError", "onError:" + errorId);
    }
    @Override
    protected boolean onRecoverableError(Context context, String errorId) {
        if (BuildConfig.DEBUG) Log.d("onError", "onError:" + errorId);
        return super.onRecoverableError(context, errorId);
    }
    
    private void setNotification(Context context) {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        Notification notification = new Notification(
                R.drawable.ic_notification,
                "受信しましたよ",
                System.currentTimeMillis()
        );
        Intent intent = new Intent(context, HelloActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
                context,
                100,
                intent,
                Intent.FLAG_ACTIVITY_NEW_TASK
        );
        notification.setLatestEventInfo(
                context,
                "タイトル",
                "内容",
                pendingIntent
        );
        notificationManager.notify(0, notification);// 適当なIDを指定する
    }
}

manifestファイルに以下の記述を追加する。

<service android:name=".GCMIntentService"/>

onCreateとかonResumeとかに以下の記述を付加する。

        GCMRegistrar.checkDevice(this);
        GCMRegistrar.checkManifest(this);
        final String regId = GCMRegistrar.getRegistrationId(this);
        if (regId.equals("")) {
            GCMRegistrar.register(this, "your sender id");
        }
        else {
            Log.v("onResume", "Already registered");
        }

■サーバー側の実装

node-gcmを使う。ToothlessGear / node-gcmh2soft / node-gcmのどちらでも問題ないがnodeのバージョンの関係で今回は前者を使う。後者の方がコールバック関数のerrorの引数がnodeっぽくて良いと思われる。

var gcm = require('node-gcm');
var sender  = new gcm.Sender('YOUR_API_KEY');
var registrationIds = ['device_registration_id'];

var message = new gcm.Message();
message.addData('content', 'text');
message.collapseKey    = 'hogehoge';
message.delayWhileIdle = true;
message.timeToLive = 3;

sender.send(message, registrationIds, 3, function (result) {
  console.log(result);
}); 

参考