問題が発生したためアプリを終了します。(アンドロイド地図アプリの開発 その11)

前回、ダイアログを出してファイルを選択することをやってみましたが、最終的に実機のSDカードのファイル選択がうまくいきませんでした。
alasixosaka.hatenablog.com
仕方がないので、ファイルマネージャーを使った方法に戻そうと、実機でプログラムを動かしていると、「問題が発生したためXXXXを終了します」というメッセージが表示されてアプリが落ちてしまう現象に遭遇してしまった。このプログラム、エミュレータでは全然何ともないのに、実機(XperiaZ4)で動かそうとすると何故だか止まってしまう。デバッガーを使って調べてみても今一原因がよくわからない(へぼいのでデバッガーのコメントに書いてあることが理解できないというのもある)。
そこで、プログラムを古いものに変えていって様子を見ると、タイル抜けを対策し、回転ができるようにしたシンプルなプログラムまで遡ると問題なく動くことが分かった。そのあとやったことは、GPSを使って現在地を取得することだったので、その辺が怪しそう。ただ、実機でGPSを使う場合は、マップを日本の地図にしてあげないと、単なる空白を表示することになるので、まずは、シンプルな回転表示のマップに日本地図を表示できるように、ファイルマネージャーを使ったファイル選択機能を実装することにした。
やり方はintentを発行して、ファイルマネージャーを起動する方法。
まず、メインアクティビティのレイアウトファイル(activity_main.xml)にボタンを一つ追加する。

    <Button
        android:id="@+id/btMapFileSelect"
        android:layout_width="214dp"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginStart="105dp"
        android:layout_marginLeft="105dp"
        android:layout_marginTop="101dp"
        android:text="Map File Select" />

そして、メインアクティビティ本体のリスナーの部分に”Map File Select"ボタンをクリックしたときの処理を追記する。

    private class BtListener implements View.OnClickListener {
        @Override
        public void onClick(View view){
            int id = view.getId();
            switch (id) {
                case R.id.btRotationalView:
                    Intent RMV = new Intent(getApplicationContext(),RotateMapViewer.class);
                    startActivity(RMV);
                    break;
                //Map File Selectボタンをクリックしたときの処理
                case R.id.btMapFileSelect:
                    SharedPreferences preferences = getSharedPreferences("DATA", Context.MODE_PRIVATE);
                    MAP_FILE = preferences.getString(fillter, "berlin.map");
                    Path = preferences.getString("path", sdPath);
                    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                    intent.setType("file/*");
                    startActivityForResult(intent, CHOSE_FILE_CODE);
                    break;
            }
        }
    }

ファイルマネージャーで選択したファイルの結果はonActivityResult()で受ける。中身は第三引数のdataに格納される。
ファイルマネージャーから持ってきたファイルパスは余分な部分が前についているので、”storage"より前の部分は切り取る。
一応拡張子をチェックして、"map"でないときは警告するようにしている。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        try {
            if (requestCode == CHOSE_FILE_CODE && resultCode == RESULT_OK){
                String filePath = data.getDataString();
                filePath = filePath.substring(filePath.indexOf("storage"));
                String decodefilePath = URLDecoder.decode(filePath, "utf-8");

                File file = new File(decodefilePath);
                String mapfile = (String) file.getName();
                String fpath = decodefilePath.substring(0,decodefilePath.indexOf(mapfile));

                String ext = mapfile.substring(mapfile.indexOf(".")+1);
                //拡張子をチェックし、"map"の場合は、SharedPreferenceにファイル名とパスを格納する。
                if (ext.equals("map")){
                    SharedPreferences preferences = getSharedPreferences("DATA", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putString("map", mapfile);
                    editor.putString("path", fpath);
                    editor.apply();
                } else {   //"map"でない場合は、警告文をトーストする。
                    Toast.makeText(this, "not map file", Toast.LENGTH_SHORT).show();
                }
                //選択したファイル名をトースト表示する。
                Toast.makeText(MainActivity.this, mapfile, Toast.LENGTH_SHORT).show();
            }
        } catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
    }

選択したファイルを反映させるために、前回同様、SmplesBaseActivity.javaのマップファイル選択のところを書き換える。

   @Override
    protected String getMapFileName() {
        SharedPreferences preferences = getSharedPreferences("DATA", Context.MODE_PRIVATE);

        String mapfile = (MainActivity.launchUrl == null) ? null : MainActivity.launchUrl.getQueryParameter("mapfile");
        if (mapfile != null) {
            return mapfile;
        }
        //ベルリンマップの代わりにSharedPreferenceのマップファイル名を返す。
        return preferences.getString("map", "berlin.map");
        //return "berlin.map";
    }

ところが、これでSDカードのマップファイルを選択して、地図を表示させようとするとまたしても、「問題が発生したためXXXXを終了します」と表示されて止まってしまう。
よく考えれば、ファイル名を正しく返すようにしただけで、ファイルパスはそのままなので、本体の外部フォルダを見に行ってファイルがないというエラーになっている。
そこで、ファイルパスを返している部分を探すと、おなじSamplesBaseActivity.javaにgetMapfileDirectory()というのがあるのでそこを書き換える。

    @Override
    protected File getMapFileDirectory() {
        SharedPreferences preferences = getSharedPreferences("DATA", Context.MODE_PRIVATE);
        String mapdir = (MainActivity.launchUrl == null) ? null : MainActivity.launchUrl.getQueryParameter("mapdir");
        if (mapdir != null) {
            File file = new File(mapdir);
            if (file.exists() && file.isDirectory()) {
                return file;
            }
            throw new RuntimeException(file + " does not exist or is not a directory (configured in launch URI " + MainActivity.launchUrl + " )");
        }
        //SharedPreferenceのファイルパスを返す。
        return new File(preferences.getString("path", Environment.getExternalStorageDirectory().getPath()));
        //return super.getMapFileDirectory();
    }

これでエラーはなくなったが、地図のセンターがベルリンのままなので、地図が表示されない。
そこで、伊勢在住のプログラマーさんのプログラムを参考にOverlayMapViewer.javaのcreateMapvier()を書き換える。

    @Override
    protected void createMapViews() {
    ~省略~
    //initializePosition(mapView.getModel().mapViewPosition);
       mapView.setCenter(new LatLong(34.491297, 136.709685)); // 伊勢市駅

これで、日本地図が一応見れる形になった。
また、実機で検証しながら地道に機能を追加していくことにする。

参考にしたサイト
intentからファイラーアプリ起動でファイル選択: がらくた研究室
mapsforge でポップアップするマーカーを試す - プログラマーのメモ書き
SharedPreferencesをサクッと使う - Qiita