expectの使い方メモ

ftpしか使えないサーバーがあって、しかもユーザー名に@が入ってたりとかパスワードに:が入ってたりとかして嫌がらせのように一行のエイリアスが作れなかった。もしかしたら一行で接続する方法あるのかもしれないが知識不足でできなかった。ので、expectを書くことにした。

#!/usr/bin/expect

set NAME "hogehoge@hogehoge"
set PASSWORD "fugafuga:piyopiyo"

set timeout 5

spawn env LANG=C ftp example.com
expect {
    "Name" {
        send ${NAME}\n
        exp_continue
    }
    "Password:" {
        send ${PASSWORD}\n
        exp_continue
    }
}

expect {
    "ftp>" {
        interact
        exit 0
    }
}

シェルでexpectごとスクリプト化して書く方法もあるみたいだけど、複数のプロンプトだと途中で何故かとまる。書き方が悪いのだろう。
結局expectで書いて解決した。

env LANG=C というのはプロンプトをデフォルトの言語(英語)で出させるという意味らしい。

ネタ元: Linuxの対話がめんどくさい?そんな時こそ自動化だ!-expect編-

XamarinでAndroidの位置情報をバックグラウンドのServiceでとります

普通にLocationManager使っている。 世の中的にはFusedLocation使えってことなんだろうけどそうもいかない場合もきっとあるはず。

バックグラウンドで位置情報取り続けるには、 Activityクラスに加えてServiceクラス、BroadcastReceiverクラスが必要っぽい。

まずはActivityから。

public class MyActivity : Activity
{
    private Intent ServiceIntent;
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // ...

        ServiceIntent = new Intent(this, typeof(MyLocationService));
        StartService(ServiceIntent);

        var receiver = new MyLocationReceiver();
        RegisterReceiver(receiver, new IntentFilter());
        var handler = new MyHandler(this);
        receiver.RegisterHandler(handler);
    }

    // ...

    public class MyHandler : Handler
    {
        private MyActivity Parent;

        public MyHandler(MyActivity parent)
        {
            Parent = parent;
        }

        public override void HandleMessage(Message msg)
        {
            var bundle = msg.Data;
            var latitude = bundle.GetDouble("latitude");
            var longitude = bundle.GetDouble("longitude");
            var message = string.Format("lat: {0}, lng: {1}, latitude,
longitude");
            System.Diagnostics.Debug.WriteLine(message);

            // 取得したlatitudeとlongitudeを使って好きな処理をする
            var loc = new MyLocation(latitude, longitude);
            // ...
        }
    }
}

C#では何かを継承した匿名クラスを作れないみたいなので内部クラス作って対応してる。

MyLocationは簡単にこんな感じ。jsonシリアライズしてサーバーとか他のActivityとかとやり取りしたいのでDataContract属性つけてる。

[DataCantract]
public class MyLocation
{
    [DataMember(Name = "latitude")]
    public double Latitude;
    [DataMember(Name = "longitude")]
    public double Longitude;

    public MyLocation(double latitude, double longitude)
    {
        Latitude = latitude;
        Longitude = longitude;
    }
}

次はServiceクラス。

[Service]
public class MyLocationService : Service
{
    private static string TAG = "MyLocationService";
    private LocationManager mLocationManager = null;
    private static int INTERVAL = 1000;
    private static float DISTANCE = 0f;
    public PendingIntent mPendingIntent;

    public override Android.OS.IBinder OnBind(Android.Content.Intent intent)
    {
        return null;
    }

    public override StartCommandResult
OnStartCommand(Android.Content.Intent intent, StartCommandFlags flags,
int startId)
    {
        mLocationManager =
(LocationManager)getSystemService(Context.LocationService);
        var receiver = new Intent(this, typeof(MyLocationReceiver));
        mPendingIntent = PendingIntent.GetBroadCast(this, 0, receiver,
PendingintentFlags.UpdateCurrent);

        try
        {
            MLocationManager.RequestLocationUpdates(LocationManager.GpsProvider,
INTERVAL, DISTANCE, mPendingIntent);
        }
        catch (Exception e)
        {
            Log.Debug(TAG, e.Message);
        }

        return StartCommandResult.Sticky;
    }

    public override void OnDestroy()
    {
        mLocationManager.RemoveUpdates(mPendingIntent);
        base.OnDestroy();
    }
}

Serviceに関してはほぼここ のソースを使わせて頂きました。

で、Receiver。

[BroadcastReceiver(Enabled = true, Exported = false)]
public class MyLocationReceiver : BroadcastReceiver
{
    public const string TAG = "MyLocationReceiver";
    public static MyActivity.MyHandler Handler;

    public MyLocationReceiver()
    {}

    public override void OnReceive(Context context, Intent intent)
    {
        string locationKey = LocationManager.KeyLocationChanged;
        if (intent.HasExtra(locationKey))
        {
            Location loc = (Location)intent.Extras.Get(locationKey);
            var location = new MyLocation(loc.Latitude, loc.Longitude);

            var task = Task.Run( async () =>
            {
                // ハンドラーにメッセージ送ることでアクティビティ側で処理可能になる
                var msg = CreateLocationMessage(loc.Latitude, loc.Longitude);
                Handler.SendMessage(msg);
                // なんか非同期な処理をする。locationをjsonシリアライズしたりとかhttpで送ったりとか
            });
        }
    }

    public void RegisterHandler(MyActivity.MyHandler handler)
    {
        Handler = handler;
    }

    private Message CreateLocationMessage(double latitude, double longitude)
    {
        var msg = new Message();
        var data = new Bundle();
        data.PutDouble("latitude", latitude);
        data.PutDouble("longitude", longitude);
        msg.Data = data;
        return msg;
    }
}

ハンドラーに関してはここ を参考にさせていただきました。

正直何がどうなって動いているのかよくわからないが備忘のため書いておく。

XamarinでFragmentがinflateできなくてキレそうになった

XamarinでAndroidのFragment使おうと思って調べながら書いてたわけ。

<fragment
    android:name="com.example.MyFragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

当然公式ガイド (古いけど)(古いのが放置されてるというのも問題だけど)にもこんなのが載ってる

<?xml version="1.0" encoding="utf-8"?>
<fragment android:name="com.xamarin.sample.fragments.TitlesFragment"
            android:id="@+id/titles_fragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

そしたらやっぱり、android:name属性はAndroidのパッケージ名+クラス名を書くんやな、て思うじゃん?

そしたらInflateExceptionが出て全然表示できない。それで半日くらいググるのに費やしたけど解決しない。

最終的にはAndroidのパッケージ名ではなくC#のnamespaceを使ったら解決した。糞が。

<fragment
    android:name="MyApplication.Droid.MyFragment"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

なんなんだよマジで。なんでググってもここでハマった人いないの? 俺のプロジェクト名の付け方がおかしいのか? パッケージ名みたいにプロジェクト名もcom.example…とかやるべきなのか?

Android Studioならこんなことでハマることはありえない。 完全に納得がいかなかった。Xamarin辞めたい。

XamarinでAndroidのServiceクラスが使えないとき

[Android] FusedLocationProviderApi を使って位置情報を取得

を参考にVisualStudio for Mac(Xamarin)でServiceとBroadcastReceiverを使おうとしたら呼ばれない。ブレークポイントすらも止まらない。AndroidManifest.xmlに記述が抜けてるのかと思ったがそれも違う。

なぜなんだと思ってXamarinの公式サンプル見たらわかった。

クラスの定義にカスタム属性つけるだけだった。。

Service
[Service]
public class MyService : Service
{
    /* ... */
}
BroadcastReceiver
[BroadcastReceiver(Enabled = true, Exported = false)]
public class MyReceiver : BroadcastReceiver
{
    /* ... */
}

これ書いとけばAndroidManifest.xmlには何も書かなくても動いた。

Xamarinはこういうくだらないけど知らないとハマるポイントが結構あってムカつく。Xamarinは生産性に寄与しない気がする。

Xamarinの良いところって結局C#で書けるということくらいだろうと思う。

正直C#よりもSwiftとKotlin使えるのであればそっち使えたほうが嬉しい。。

C#のHttpClientでPOSTしてresponse bodyを受け取る

リクエスト送信
async public Task<string> HttpPost(string url)
{
    var client = new HttpClient();
    var content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        /**
         * POSTするデータを書く
         * { "key", value }
         * のような感じで
         */
    });

    var response = await client.PostAsync(url, content);
    return await response.Content.ReadAsStringAsync();
}

responseにはStatusCodeとかHeadersも入ってる。

レスポンス取得
var url = "http://127.0.0.1:8080";
var task = Task.Run(() => {
    return HttpPost(url);
});
System.Console.WriteLine(task.Result);

ググってみたら他所のサイトのサンプルコードには

var task = await HttpPost(url);

で値が取れるかのように書いてあるところもあったけど、自分の場合は無理だった(アプリが止まった)

.NETのWEBアプリじゃなくてXamarinのスマホアプリだからだろうか。。

neovimで言語ごとのインデントとかの設定をする方法

まあマニュアル読めば書いてある話ではあるんだけど、そこらへんうまいこと日本語で解説してくれてる人がいるかと思ったらあんまりいないみたいだったので自分用にメモっておく。

1. runtimeディレクトリにftpluginディレクトリを作る

Unix系のシステムでは通常これは “~/.config/nvim” になるらしい。

mkdir -p ~/.config/nvim/ftplugin

runtimeディレクトリがわかんない場合は、vim上で

:set runtimepath?

とコマンド打てば表示される。
表示された中で一番最初に出てくるパスを普通は使うものらしい。

2. さっき作ったftpluginディレクトリに “ファイルタイプ名.vim” ファイルを作る

で、そのファイルの中身には普通にinit.vim(.vimrc)みたいにインデントとかの設定を書くわけだけど、:setじゃなくて:setlocalとしておくとそのバッファだけに設定が反映されるので他のファイルタイプに影響がないようにできるらしい。

e.g.
setlocal shiftwidth=2

同様にキーマップについてもbufferローカルな設定にしとくと良いらしい。

ftplugin/javascript.vim
let maplocalleader = ","
map <buffer> <LocalLeader>A o新しい行だよ!<ESC>

上記を設定した後に.jsのファイルを開いて",A(shift+a)“とつづけて入力すると、カーソルの下の行に「新しい行だよ!」と追加されて、ノーマルモードに戻っている。

ネットカフェで勉強することの良い点悪い点

ネットカフェで勉強することの良い点悪い点

最近土日とかにネットカフェで勉強することが増えた。家にいると勉強しないから。

でもネットカフェが勉強する場所として最適というわけではない。家にいるよりは気持ちを切り替えて勉強できるんだけど勉強を阻害する要因も同時にある。それについて感じたことをメモ。

  • 良い点

    • 気持ちを切り替えられる

      家にいると勉強してる最中であっても部屋のゴミが気になったり洗濯物を始めてしまったり本を読み始めてしまったりYouTubeを観始めてしまったりしてなかなか固まった作業の時間が取れない。

      対してネットカフェだと個室なので他に注意を向ける対象がない。MacBookに集中して向き合うことができる。

    • 金がかかっているので頑張れる

      ネットカフェの利用にはカネがかかる。そこで遊んでしまったらカネが無駄になってしまうという意識が働くため、家にいて作業するより頑張ることができる。

    • 飲み物が飲める

      これは意外に良い。所詮ネカフェで飲める飲み物なんてインスタントのコーヒーとか体に悪いジュースとかだから品質が良いというわけではないんだけど、インスタントであってもコーヒーを一杯飲んでから作業を始めると集中できる。普段カフェイン絶ちをしているのでなおさら効く。

      もちろん家にいてもコーヒーくらい沸かせば良いんだけど、ボタン一つで出てくるので意識を妨げられる度合いが少ない。

    • 腹が減ったら飯を注文できる

      これも飲み物と同じで、意識を妨げられずに食事を取れるというのが良い。家で腹が減ると、買いに行くか作るかせねばならずそこで作業が中断させられる。

      しかもネカフェの飯は安価である。栄養価スコアは悪そうだけど。

    • 休憩時の気分転換アイテムがある

      マンガは豊富に揃っているし、雑誌、新聞などある。店によってはテレビとか映画とかある。色々あるんだけどやはりマンガが一番気分転換としては良い。休憩時に読みたいマンガ4、5巻取ってブースに入るといい感じだ。

  • 悪い点

    • 時間を気にしないといけない

      多くのネカフェは3時間とか6時間のコースがある。最近は自動でプラン切り替えてくれる店も多いけど、入店時にプラン決めとかないといけない店も多い。3時間コースで入って3時間を超えてしまうと延長料がバカ高くて割高になる。

      作業始めて数時間経ってから調子が出てくるので、ちょうどいいときにもう出る時間だったりする。

    • ネットが繋がらないときがある

      MacBookにはLANケーブルを刺す穴がない。無線でネットに繋がないといけないわけだが、店によっては(というか大抵の店で)Wifiの環境が良くない。席次第でブチブチ切れたり、FreeSpotしかない店もある。せっかくはるばるやってきたのにネット環境なしでは今日日何も作業できないに等しい。ネットが繋がらなくなったりしたらあとはもうマンガを読むしかなくなり勉強できない。

    • うるさいやつがいたときに殺意が湧く

      たまにエンターキーめちゃくちゃうるさく叩くやつがいると殺意が湧く。イヤホンを必ず持ち込んだほうが良い。

      また、咳払いがうるさいやつとかいびきがうるさいやつとかもいる。店によってはカラオケルームの子供がうるさいとか、ビリヤードコーナーの高校生がうるさいとかもある。本当にイヤホン必携。

    • マンガを読んでしまう

      マンガが置いてあるのはメリットでもありデメリットでもある。休憩しようと思ってマンガを読み始めると、続きが気になって結局最後まで読んでしまい、ほとんど作業が進まなかった、という経験も何回かある。長編物のマンガには気をつけたい。

まとめ

まずは家で勉強やってみて、はかどらないというときにネットカフェとかに行くと良いのかもしれない。休日家で勉強してるとせっかくの休みなのに俺は何をやってるんだろうか、とか考えてしまうけど、ネカフェに行くと適度に出かけた感も得られて精神衛生上も良い。

勉強は孤独な作業なので、その分外に出かけたりとか人と会話したりする機会を意識して作ると良いのだと思う。