覚書 Android Studioのアンインストール方法 (Mac版)

Android Studioのアンインストールで、Windows版はアンインストーラーで綺麗に削除できるが、Mac版は、Applicationフォルダからアプリを削除しても、SDKなど別のフォルダにインストールされているので、綺麗にならないってことで、コンソールを立ち上げて、下記のコマンドで削除していきます。

rm -Rf /Applications/Android\ Studio.app
rm -Rf ~/Library/Preferences/AndroidStudio*
rm ~/Library/Preferences/com.google.android.studio.plist
rm -Rf ~/Library/Application\ Support/AndroidStudio*
rm -Rf ~/Library/Logs/AndroidStudio*
rm -Rf ~/Library/Caches/AndroidStudio*
rm -Rf ~/.gradle
rm -Rf ~/.android

SDKのツールは

rm -Rf ~/Library/Android*

でOK!

WebRTC Data Channel Sample for Android を作ってみた

ここんところ、WebRTCのアプリを作ろうとして、四苦八苦していました。

やっと、データチャンネルで送受信するアプリができたので、ここに公開します。

開発環境ですが
Android Studio 1.2.1.1 build 141.19032520
を使って開発しています。

普通にプロジェクトを作成します。

まず、WebRTCのライブラリをインポートする為の設定を行います。

app配下にあるbuild.gradleのdependenciesに下記の一行を入れるだけ

compile 'io.pristine:libjingle:9357@aar'

これでなんと、ネイティブのsoライブラリまで持ってきてくれます(なんと便利な!)

150610-0006

次に、AndroidManifest.xmlにパーミッションを追加します

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

150610-0003

これで、準備OK、ここからは、MainActivity.javaのソースをそのまま掲載しちゃいます。

package jp.eguchi.android.datachannelsample;

// WebRTC Data Channel Sample Program
// Programed by Kazuyuki Eguchi

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

import org.json.JSONObject;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;


public class MainActivity extends Activity {

    final private String TAG = "sample";

    PeerConnectionFactory factory = null;

    PeerConnection pc1 = null;
    PeerConnection pc2 = null;

    DataChannel dc1 = null;
    DataChannel dc2 = null;

    // pc1用のObserver
    PeerConnection.Observer pcob1 = new PeerConnection.Observer() {
        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.d(TAG, "pcob1 onSignalingChange() " + signalingState.name());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.d(TAG, "pcob1 onIceConnectionChange() " + iceConnectionState.name());
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.d(TAG, "pcob1 onIceGatheringChange() " + iceGatheringState.name());
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            Log.d(TAG, "pcob1 onIceCandidate()");

            JSONObject json = new JSONObject();

            String mes = null;

            try {
                json.put("type", "candidate");
                json.put("sdpMLineIndex", iceCandidate.sdpMLineIndex);
                json.put("sdpMid", iceCandidate.sdpMid);
                json.put("candidate", iceCandidate.sdp);

                mes = json.toString();

                Log.d(TAG, mes);

                // ここで、 WebSocket等で相手側に mesを送る
                // 受信側では

                JSONObject json2 = new JSONObject(mes);
                IceCandidate candidate = new IceCandidate(json2.getString("sdpMid"), json2.getInt("sdpMLineIndex"), json2.getString("candidate"));
                pc2.addIceCandidate(candidate);

            } catch (org.json.JSONException ex) {
                Log.d(TAG, ex.toString());
            }
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.d(TAG, "pcob1 onAddStream()");
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.d(TAG, "pcob1 onRemoveStream");
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
            Log.d(TAG, "pcob1 onDataChannel()");
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.d(TAG, "pcob1 onRenegotiationNeeded()");
        }
    };

    // pc2用のObserver
    PeerConnection.Observer pcob2 = new PeerConnection.Observer() {
        @Override
        public void onSignalingChange(PeerConnection.SignalingState signalingState) {
            Log.d(TAG, "pcob2 onSignalingChange() " + signalingState.name());
        }

        @Override
        public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
            Log.d(TAG, "pcob2 onIceConnectionChange() " + iceConnectionState.name());
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
            Log.d(TAG, "pcob2 onIceGatheringChange() " + iceGatheringState.name());
        }

        @Override
        public void onIceCandidate(IceCandidate iceCandidate) {
            Log.d(TAG, "pcob2 onIceCandidate()");

            JSONObject json = new JSONObject();

            String mes = null;

            try {
                json.put("type", "candidate");
                json.put("sdpMLineIndex", iceCandidate.sdpMLineIndex);
                json.put("sdpMid", iceCandidate.sdpMid);
                json.put("candidate", iceCandidate.sdp);

                mes = json.toString();

                Log.d(TAG, mes);

                // ここで、 WebSocket等で相手側に mesを送る
                // 受信側では

                JSONObject json2 = new JSONObject(mes);
                IceCandidate candidate = new IceCandidate(json2.getString("sdpMid"), json2.getInt("sdpMLineIndex"), json2.getString("candidate"));
                pc1.addIceCandidate(candidate);

            } catch (org.json.JSONException ex) {
                Log.d(TAG, ex.toString());
            }
        }

        @Override
        public void onAddStream(MediaStream mediaStream) {
            Log.d(TAG, "pcob2 onAddStream()");
        }

        @Override
        public void onRemoveStream(MediaStream mediaStream) {
            Log.d(TAG, "pcob2 onRemoveStream");
        }

        @Override
        public void onDataChannel(DataChannel dataChannel) {
            Log.d(TAG, "pcob2 onDataChannel()");
            dc2 = dataChannel;
            dc2.registerObserver(dc2o);
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.d(TAG, "pcob2 onRenegotiationNeeded()");
        }
    };

    SdpObserver so1 = new SdpObserver() {
        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            Log.d(TAG, "so1 onCreateSuccess()");

            pc1.setLocalDescription(so1, sessionDescription);

            JSONObject json = new JSONObject();

            String mes = null;

            try {
                json.put("type", sessionDescription.type.toString().toLowerCase());
                json.put("sdp", sessionDescription.description);

                mes = json.toString();

                Log.d(TAG, mes);

                // ここで、 WebSocket等で相手側に mesを送る
                // 受信側では

                JSONObject json2 = new JSONObject(mes);
                String type = json2.getString("type");
                String sdp = json2.getString("sdp");

                SessionDescription sdp2 = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp);

                pc2.setRemoteDescription(so2, sdp2);
                MediaConstraints constraints = new MediaConstraints();
                pc2.createAnswer(so2, constraints);

            } catch (org.json.JSONException ex) {
                Log.d(TAG, ex.toString());
            }
        }

        @Override
        public void onSetSuccess() {
            Log.d(TAG, "so1 onCreateSuccess()");
        }

        @Override
        public void onCreateFailure(String s) {
            Log.d(TAG, "so1 onCreateFailure() " + s);
        }

        @Override
        public void onSetFailure(String s) {
            Log.d(TAG, "so1 onSetFailure() " + s);
        }
    };

    SdpObserver so2 = new SdpObserver() {
        @Override
        public void onCreateSuccess(SessionDescription sessionDescription) {
            Log.d(TAG, "so2 onCreateSuccess()");
            pc2.setLocalDescription(so2, sessionDescription);

            JSONObject json = new JSONObject();

            String mes = null;

            try {
                json.put("type", sessionDescription.type.toString().toLowerCase());
                json.put("sdp", sessionDescription.description);

                mes = json.toString();

                Log.d(TAG, mes);

                // ここで、 WebSocket等で相手側に mesを送る
                // 受信側では

                JSONObject json2 = new JSONObject(mes);
                String type = json2.getString("type");
                String sdp = json2.getString("sdp");

                SessionDescription sdp2 = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type), sdp);

                pc1.setRemoteDescription(so1, sdp2);

            } catch (org.json.JSONException ex) {
                Log.d(TAG, ex.toString());
            }
        }

        @Override
        public void onSetSuccess() {
            Log.d(TAG, "so2 onCreateSuccess()");
        }

        @Override
        public void onCreateFailure(String s) {
            Log.d(TAG, "so2 onCreateFailure() " + s);
        }

        @Override
        public void onSetFailure(String s) {
            Log.d(TAG, "so2 onSetFailure() " + s);
        }
    };

    DataChannel.Observer dc1o = new DataChannel.Observer() {
        @Override
        public void onStateChange() {
            Log.d(TAG, "da1o onStateChange() " + dc1.state().name());

            if (dc1.state() == DataChannel.State.OPEN) {
                String data = "from dc1";
                ByteBuffer buffer = ByteBuffer.wrap(data.getBytes());
                dc1.send(new DataChannel.Buffer(buffer, false));
            }
        }

        @Override
        public void onMessage(DataChannel.Buffer buffer) {
            Log.d(TAG, "da1o onMessage()");

            if (buffer.binary == false) {
                int limit = buffer.data.limit();
                byte[] datas = new byte[limit];
                buffer.data.get(datas);
                String tmp = new String(datas);
                Log.d(TAG, tmp);
            }
        }
    };

    DataChannel.Observer dc2o = new DataChannel.Observer() {
        @Override
        public void onStateChange() {
            Log.d(TAG, "da2o onStateChange() " + dc2.state().name());

            if (dc2.state() == DataChannel.State.OPEN) {
                String data = "from dc2";
                ByteBuffer buffer = ByteBuffer.wrap(data.getBytes());
                dc2.send(new DataChannel.Buffer(buffer, false));
            }

        }

        @Override
        public void onMessage(DataChannel.Buffer buffer) {
            Log.d(TAG, "da2o onMessage()");

            if (buffer.binary == false) {
                int limit = buffer.data.limit();
                byte[] datas = new byte[limit];
                buffer.data.get(datas);
                String tmp = new String(datas);
                Log.d(TAG, tmp);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d(TAG, "onCreate()");

        PeerConnectionFactory.initializeAndroidGlobals(getApplicationContext(), true, false, false, null);
        factory = new PeerConnectionFactory();
    }

    @Override
    protected void onResume() {
        super.onResume();

        Log.d(TAG, "onResume()");

        // STRNサーバは Google様のを使わせてもらう。
        List<PeerConnection.IceServer> iceServers = new LinkedList<PeerConnection.IceServer>();
        iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));

        MediaConstraints constraints = new MediaConstraints();

        pc1 = factory.createPeerConnection(iceServers, constraints, pcob1);
        pc2 = factory.createPeerConnection(iceServers, constraints, pcob2);

        // offerする前にデータチャネルを作成する
        dc1 = pc1.createDataChannel("RTCDataChannel", new DataChannel.Init());
        dc1.registerObserver(dc1o);

        // Offerする
        pc1.createOffer(so1, constraints);
    }

    @Override
    protected void onPause() {
        super.onPause();

        Log.d(TAG, "onPause()");

        if (dc1 != null) {
            dc1.close();
            dc1.unregisterObserver();
        }

        if (dc2 != null) {
            dc2.close();
            dc2.unregisterObserver();
        }

        if (pc1 != null) {
            pc1.close();
        }

        if (pc2 != null) {
            pc2.close();
        }
    }
}

実行した際のLogcatのログ

06-10 17:44:58.719    4149-4149/jp.eguchi.android.datachannelsample D/sample﹕ onCreate()
06-10 17:44:58.723   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ AttachCurrentThreadIfNeeded::ctor@[tid=11577]
06-10 17:44:58.723   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ Attaching thread to JVM
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JVM::environment@[tid=11577]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::ctor@[tid=11577]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ ctor@[tid=11577]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::RegisterNatives(org/webrtc/voiceengine/WebRtcAudioManager)
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::ctor@[tid=11577]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::NewObject@[tid=11577]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioManager﹕ ctor@[name=Thread-1038, id=1038]
06-10 17:44:58.724   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioManager﹕ Nexus 5 is blacklisted for HW AEC usage!
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ OnCacheAudioParameters@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ hardware_aec: 0
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ low_latency_output: 1
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ sample_rate: 48000
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ channels: 1
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ output_buffer_size: 240
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ input_buffer_size: 1920
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ GlobalRef::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ JavaAudioManager::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ IsLowLatencyPlayoutSupported()
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample W/AudioManager﹕ NOTE: OpenSL ES output is currently disabled!
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ AttachCurrentThreadIfNeeded::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JVM::environment@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioTrackJni﹕ ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::RegisterNatives(org/webrtc/voiceengine/WebRtcAudioTrack)
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::NewObject@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioTrack﹕ ctor@[name=Thread-1038, id=1038]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioTrack﹕ Android SDK: 22, Release: M, Brand: google, Device: hammerhead, Id: MPZ44Q, Hardware: hammerhead, Manufacturer: LGE, Model: Nexus 5, Product: hammerhead
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ GlobalRef::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ AttachCurrentThreadIfNeeded::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JVM::environment@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/AudioRecordJni﹕ ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ JNIEnvironment::RegisterNatives(org/webrtc/voiceengine/WebRtcAudioRecord)
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::ctor@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ NativeRegistration::NewObject@[tid=11577]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioRecord﹕ ctor@[name=Thread-1038, id=1038]
06-10 17:44:58.726   4149-11577/jp.eguchi.android.datachannelsample D/JVM﹕ GlobalRef::ctor@[tid=11577]
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ SetActiveAudioLayer(5)@[tid=11577]
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ delay_estimate_in_milliseconds: 150
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioTrackJni﹕ AttachAudioBuffer@[tid=11577]
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioTrackJni﹕ SetPlayoutSampleRate(48000)
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioTrackJni﹕ SetPlayoutChannels(1)
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioRecordJni﹕ AttachAudioBuffer
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioRecordJni﹕ SetRecordingSampleRate(48000)
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioRecordJni﹕ SetRecordingChannels(1)
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioRecordJni﹕ total_delay_in_milliseconds: 150
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/AudioManager﹕ Init@[tid=11577]
06-10 17:44:58.727   4149-11577/jp.eguchi.android.datachannelsample D/WebRtcAudioManager﹕ init@[name=Thread-1038, id=1038]
06-10 17:44:58.731    4149-4149/jp.eguchi.android.datachannelsample D/sample﹕ onResume()
06-10 17:44:59.157   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onRenegotiationNeeded()
06-10 17:44:59.158   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so1 onCreateSuccess()
06-10 17:44:59.395   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onSignalingChange() HAVE_LOCAL_OFFER
06-10 17:44:59.396   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ {"type":"offer","sdp":"v=0\r\no=- 113918549713234061 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\nm=application 9 DTLS\/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:lw3\/LpT+Giz61KGg\r\na=ice-pwd:QxRiyQgLEFnfWEsJ0p5+6G3s\r\na=fingerprint:sha-256 B8:AB:2D:09:BB:01:23:04:44:64:81:1E:F6:65:24:25:74:A5:6C:F0:62:8B:96:3E:86:83:E2:ED:A9:33:E6:FA\r\na=setup:actpass\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n"}
06-10 17:44:59.398   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onSignalingChange() HAVE_REMOTE_OFFER
06-10 17:44:59.398   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceGatheringChange() GATHERING
06-10 17:44:59.398   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so1 onCreateSuccess()
06-10 17:44:59.398   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so2 onCreateSuccess()
06-10 17:44:59.399   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so2 onCreateSuccess()
06-10 17:44:59.404   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onSignalingChange() STABLE
06-10 17:44:59.406   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ {"type":"answer","sdp":"v=0\r\no=- 3088308566766388678 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\nm=application 9 DTLS\/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\nb=AS:30\r\na=ice-ufrag:4gco7NuR3wheyF6s\r\na=ice-pwd:Fv95\/TF5FvTbGMCcISNSKNf3\r\na=fingerprint:sha-256 31:EE:E9:19:85:5B:02:5F:CC:80:A3:FC:F7:64:EC:71:B6:01:4E:D0:35:1B:74:8E:D1:30:05:43:49:0D:52:55\r\na=setup:active\r\na=mid:data\r\na=sctpmap:5000 webrtc-datachannel 1024\r\n"}
06-10 17:44:59.410   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onSignalingChange() STABLE
06-10 17:44:59.412   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceConnectionChange() CHECKING
06-10 17:44:59.412   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceCandidate()
06-10 17:44:59.412   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ {"type":"candidate","sdpMLineIndex":0,"sdpMid":"data","candidate":"candidate:3209757399 1 udp 2122260223 10.49.120.146 39965 typ host generation 0"}
06-10 17:44:59.415   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceConnectionChange() CHECKING
06-10 17:44:59.415   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceGatheringChange() GATHERING
06-10 17:44:59.415   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so2 onCreateSuccess()
06-10 17:44:59.415   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ so1 onCreateSuccess()
06-10 17:44:59.416   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceCandidate()
06-10 17:44:59.416   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ {"type":"candidate","sdpMLineIndex":0,"sdpMid":"data","candidate":"candidate:3209757399 1 udp 2122260223 10.49.120.146 41101 typ host generation 0"}
06-10 17:44:59.444   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceGatheringChange() COMPLETE
06-10 17:44:59.453   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceGatheringChange() COMPLETE
06-10 17:44:59.486   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceConnectionChange() CONNECTED
06-10 17:44:59.487   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceConnectionChange() COMPLETED
06-10 17:44:59.488   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da1o onStateChange() OPEN
06-10 17:44:59.489   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceConnectionChange() CONNECTED
06-10 17:44:59.493   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onDataChannel()
06-10 17:44:59.494   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da2o onStateChange() OPEN
06-10 17:44:59.495   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da2o onMessage()
06-10 17:44:59.495   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ from dc1
06-10 17:44:59.496   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da1o onMessage()
06-10 17:44:59.496   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ from dc2
06-10 17:45:03.231    4149-4149/jp.eguchi.android.datachannelsample D/sample﹕ onPause()
06-10 17:45:03.231   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da1o onStateChange() CLOSING
06-10 17:45:03.233   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da1o onStateChange() CLOSED
06-10 17:45:03.233   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da2o onStateChange() CLOSING
06-10 17:45:03.234   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ da2o onStateChange() CLOSED
06-10 17:45:03.236   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onIceConnectionChange() CLOSED
06-10 17:45:03.236   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob1 onSignalingChange() CLOSED
06-10 17:45:03.239   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onIceConnectionChange() CLOSED
06-10 17:45:03.240   4149-11578/jp.eguchi.android.datachannelsample D/sample﹕ pcob2 onSignalingChange() CLOSED

以上、ご参考までに

これで、WebRTCのアプリが増えるといいなあ〜

スマホでiBeaconのパラメータを変更できるアプリを作りました!(REBL600FR向け)

2014年3月23日(日)に開催されたBLE座談会に参加しました。
いつもお世話になっているRunning Electronicsの渋谷さんもいらしていまして、FRISKビーコンを持っていました。
私も、持ってきましたよって思ってみたのですが、なんと、BLEのチップはBL600になっているのです!
ファームウェア(ユーザソフトウェア)の書き換え方法が、GR-KURUMIみたいに、UART経由で出来るということで、
FRISKケースに入るBL600基板 REBL600FR』を購入させていただきました!

さてと、ファームウェアの開発するぞーと書き込まれているファームウェアのソースコードを眺めていました。
あれ!?これって、iBeaconのパラメータがBLE経由で書き換えられるようになっている。
しかも、以前作ったBLE112向けの『スマホでパラメータを書き換えられるiBeacon互換BeaconをBLE112で作ってみました。』と同じ考え方で作られているじゃないですか!
ということは、ちょっと改良するだけで、パラメータ変更するためのAndroidアプリが作れるということで、作っちゃいました!
ソースコードはいつもどおり、https://github.com/KazuyukiEguchi/BL600-iBeacon-Androidで公開させていただきました。
■アプリの使い方です。
起動すると、こんな画面が出てきます。

『検索』ボタンを押すと、FRISKビーコンを探します。
見つかると、ビーコンのBluetoothアドレスが表示されます。

ここで、一度、ビーコンの電源OFF/ONしましょう!なんでこんなことするの?と思うと思いますが、電源ONにしてから1分ぐらいしか、接続することができないので!
『接続』ボタンを押すと、ビーコンに接続を行い、現在のパラメータを取得できます。

で、書き換えたいパラメータを入力し、上から、uuid,major,minor,送信パワー(dB)になっています。
『書き込み』ボタンを押すと、反映されます。

ここで、注目なのが、送信電力を制御できるんです!
これってすごいことなんですよ。
パラメータだけ変更するビーコンが多い中、本当に送信電力が変化します!
RSSIの値が変わりますから!
ビーコンを短距離に複数置くとどうなるか?を知っている人は、送信電力を変えられることがどれだけありがたいことなのか?がわかるはずです!

GR-KURUMI β版のプロデューサーミーティングでの成果物披露

今日は、GR-KURUMIβ版のプロデューサーミーティング最終回が開催されたので、成果物の発表を行って来ました。
東京と大阪をTV会議システムで繋いで行うということで、マイPCが使えなかったこともあり、Youtube上の動画なら再生できるということで、初めてデモ動画を作りました。
動画の公開と同時に、これも初めてのスライドシェアも使って、発表資料も公開します。
スライドは
http://www.slideshare.net/egupyon/gr-kurumi
動画は
Youtube
http://youtu.be/KpUoOjfxlRg
ニコニコ動画
http://www.nicovideo.jp/watch/sm20417228
です。

Android 3.1でWiFiの設定にProxy設定が追加された。

Android 3.1で変わったこと、Proxy設定がWiFiでも利用出来るようになったみたいで、こんな画面が追加されていました。
普通にAP作って、長押しすると、画面のような設定変更が出てきます。

Proxyの詳細画面は

こんな感じで、結構細かく指定できるようです。
かなり企業向けに進化していますね。

XOOMにAndroid 3.1へのアップデート通知がやって来た。

Google I/Oで発表されたAndroid 3.1、しかも、即日でベライゾン版XOOMへのアップデート開始のお知らせもあり、私のXOOMにもいつアップデートができるのだろうとワクワクして待っていたところ、やっと、通知がやって来ました。

無事アップデートされ、バージョンも

で、念願のマウス接続をということで、何故か、Apple Magic Mouseを接続しました。ちゃんと、マウスカーソルも出来てきて、ちゃんと操作できます。
リモートアクセスをやりたかった方には、待っていた対応じゃないでしょうか?
画面見てもらえるとわかりますが、マウスカーソルが出ています。

[Android] 足跡アプリをバージョンアップしました。カレンダーアプリ対応

足跡アプリを使ってみた方から、標準搭載されているカレンダーアプリに記録できること期待してというコメントを頂いていましたので、対応してみました。
設定は簡単で、チェックボックスをONにするだけ

こんな感じでカレンダーアプリに記録されます。

アプリのダウンロードは、ここから(Android端末のみ)からどうぞ

[Android] NFCの機能をつかったセキュリティ系AndroidアプリケーションをAndroid Marketに公開しました。

nexus sを入手したこともあって、搭載されているNFCの機能を使ってみようと、対応のアプリケーションを作ってみました。
スクリーンOFF、もしくは、特定のアプリ起動時に画面をロックし、ロックを解除するためには、登録したFelicaのカードで解除するというアプリです。
ダウンロードは、ここ(Android端末のみ)からどうぞ!
使い方
起動すると、設定画面が表示されます。

『認証カードの登録』ボタンを押します。

すると、鍵として利用するカードをかざします。

すると、NFC対応アプリの一覧が表示されますので、『NFC Security』を選択します。(常にこの操作で使用するにチェックを入れると使いやすいです。)
他のNFCのアプリも使用したいというのもあると思って、ロック画面以外では、NFC対応アプリが選択できるように考慮してあります。

これで、認証カード登録完了です。
デフォルトのままだと、スクリーンOFFでロックしますので、電源ボタンを軽く押して、スクリーンをONにすると、

ロック画面で、出てくるので、登録したFelicaカードをかざすと、解除されます。

[Android]隠されている標準カメラアプリを起動するアプリをAndroid Marketに公開しました。

IS03を利用していて、思うこと。
電池残量が20%以下になるとSHカメラで写真が撮影できなくなってしまいます。
電池残量が減っているときに限って写真撮りたくなるシーンがありますよね?
実は、IS03には、Android標準のカメラアプリが隠されています。
このアプリは隠されたカメラアプリを起動するアプリケーションです。
これで、電池がなくなるまで、写真を撮ることができるようになります。
ここ(Android端末のみ)から、ダウンロードしてください。

[Android] 『アドレス帳連携』マッシュルームアプリをAndroid Marketに公開しました。

ガラパゴス携帯のFEPには、アドレス帳の情報を引用する機能が搭載されていると思います。
Android端末でも、アドレス帳の情報を引用できるように、マッシュルームアプリとして実装してみました。
良かったら、使ってみてください。
ダウンロードは、ここ(Android端末のみ)からどうぞ