1. Webアプリを読み込むAndroidアプリを作成
- Android Studioでプロジェクトを作成する。
- Blank Activityをベースにした場合、APIレベル14(Android4.0)以降で作るとActionBarActivityを継承するが、アクションバーは使用しな場合はActivityの継承に変更するのがオススメです。
- ネットアクセスのpermissionを設定する。
- AndroidManifest.xmlにpermissionを追加
<uses-permission android:name="android.permission.INTERNET" />
- 回転できないように縦方向で固定する。
- AndroidManifest.xml内の要素activityの属性に"android:screenOrientation="portrait"を追加
<activitiy ... android:screenOrientation="portrait" > ...
- アプリ起動時にjavascriptを有効にしてWebアプリを読み込む。
- WebViewを配置 (id = webView)
- 起動時のActivityクラス(Javaファイル)のonCreateメソッドに以下を追加
WebView myWebView; String urlTop = "http://www.example.com"; @Override protected void onCreate(Bundle savedInstanceState) { ... setContentView(R.layout.activity_trek_log_view); myWebView = (WebView) findViewById(R.id.webView); WebSettings webSettings = myWebView.getSettings(); //javascriptを有効にする webSettings.setJavaScriptEnabled(true); myWebView.setWebViewClient(new WebViewClient()); myWebView.loadUrl(urlTop+"index.html");}
※参考:Androidのwebviewとか。
これでWebアプリをAndroidのネイティブアプリとして起動できます。
ただ、このままではHTML5の機能が使用できないため追加の実装を行います。
2.HTML5の機能を有効化する
- HTML5のlocalStorageとapplication cacheを有効にする。
- ActivityクラスのonCreateメソッドに以下を追加
//localStorageが使用できるようにする webSettings.setDomStorageEnabled(true); //application cacheを有効にする webSettings.setAppCacheEnabled(true); //application cacheのキャッシュ先を指定 webSettings.setAppCachePath("/appcache/"); //引数は「キャッシュが存在する場合はそれを使用、ない場合はネットワーク経由で取得」 webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
- 位置情報取得を有効にする。
- AndroidManifest.xmlにpermissionを追加
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- ActivityクラスにInterface「LocationListener」を実装する
public class TrekLogView extends Activity implements LocationListener{
- LocationListenerで実装が必要なメソッドをオーバーライドする
@Override public void onLocationChanged(Location location) {} @Override public void onProviderDisabled(String provider) {} @Override public void onProviderEnabled(String provider) {} @Override public void onStatusChanged( String provider, int status, Bundle extras) {}
- HTML5のgeolocation APIを有効化
webSettings.setGeolocationEnabled(true); myWebView.setWebChromeClient(new WebChromeClient(){ @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } });
- アプリ初回起動時(onCreate)に位置情報取得を起動
LocationManager locationManager; String bestProvider; @Override protected void onCreate(Bundle savedInstanceState) { ... initLocationService(); } //初回起動時に実行する protected void initLocationService() { locationManager = (LocationManager)getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_MEDIUM); bestProvider = locationManager.getBestProvider(criteria, true); }
- アプリ復帰時(onResume)に位置情報取得を再開、停止時(onPause)に停止する
@Override protected void onResume() { super.onResume(); locationManager.requestLocationUpdates(bestProvider, 60000,1, this); } @Override protected void onPause() { super.onPause(); locationManager.removeUpdates(this); }
これでapplication cache/localStorage/geolocation APIを使ったWebアプリをAndroidのネイティブアプリとして起動できます。
※参考:
- WebView で Web Storage / Web SQL Database を利用する設定
- AndroidのWebViewをできるだけ速く表示する(キャッシュ・先読み編)
- AndroidのWebView内でgeolocationを使う
- 条件にあう位置情報プロバイダから、位置情報を利用する簡単な例
3.WebViewを使ったWebアプリの使い勝手を改善
WebアプリをAndroidアプリにしたことで使い勝手が悪いところがあるため改善します。- Android端末の「戻るボタン」でWebアプリの「戻る」を割り当てる。
- ActivityクラスのonKeyDownメソッドにオーバーライドする
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { myWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); }
- Activityクラスに以下を追加、変更。
@Override protected void onCreate(Bundle savedInstanceState) { ... myWebView.setWebViewClient(new MyWebViewClient()); ... } ... private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { boolean isMyLogPublicHost = Uri.parse(url).getHost().equals("www.example.com"); boolean isMyTestHost = Uri.parse(url).getHost().equals("www.test.com"); boolean isMyHost = isMyPublicHost || isMyTestHost; // 特定ホスト名、かつ、特定文字列を含むURLの場合はWebView内で画面遷移させる if (isMyHost && Uri.parse(url).getPath().indexOf("map.html") == -1) { return false; } Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; } }
これで一通り使えるようになりました。
※参考: Building Web Apps in WebView
4.まとめ
最後に、今回作ったソースコードをまとめて載せておきます。- AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.appspot.yamanobo.myapplication" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
- MainAcitivity.java
package com.example.myapplication; import android.content.Intent; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.net.Uri; import android.app.Activity; import android.os.Bundle; import android.view.KeyEvent; import android.webkit.GeolocationPermissions; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; public class MainActivity extends Activity implements LocationListener { WebView myWebView; String urlTop = "http://trek-log.appspot.com/"; LocationManager locationManager; String bestProvider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setContentView(R.layout.activity_main); myWebView = (WebView) findViewById(R.id.webView); WebSettings webSettings = myWebView.getSettings(); //javascriptを有効にする webSettings.setJavaScriptEnabled(true); //localStorageが使用できるようにする webSettings.setDomStorageEnabled(true); //application cacheを有効にする webSettings.setAppCacheEnabled(true); //application cacheのキャッシュ先を指定 webSettings.setAppCachePath("/appcache/"); //引数は「キャッシュが存在する場合はそれを使用、ない場合はネットワーク経由で取得」 webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); webSettings.setGeolocationEnabled(true); myWebView.setWebChromeClient(new WebChromeClient(){ @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); } }); myWebView.setWebViewClient(new MyWebViewClient()); myWebView.loadUrl(urlTop+"index.html"); initLocationService(); } //初回起動時に実行する protected void initLocationService() { locationManager = (LocationManager)getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setPowerRequirement(Criteria.POWER_MEDIUM); bestProvider = locationManager.getBestProvider(criteria, true); } @Override protected void onResume() { super.onResume(); locationManager.requestLocationUpdates(bestProvider, 60000,1, this); } @Override protected void onPause() { super.onPause(); locationManager.removeUpdates(this); } @Override public void onLocationChanged(Location location) {} @Override public void onProviderDisabled(String provider) {} @Override public void onProviderEnabled(String provider) {} @Override public void onStatusChanged( String provider, int status, Bundle extras) {} @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { myWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); } private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { boolean isMyPublicHost = Uri.parse(url).getHost().equals("www.trek-log.com"); boolean isMyTestHost = Uri.parse(url).getHost().equals("www.trek-log-dev.com"); boolean isMyHost = isMyPublicHost || isMyTestHost; // 特定ホスト名、かつ、特定文字列を含むURLの場合はWebView内で画面遷移させる if (isMyHost && Uri.parse(url).getPath().indexOf("map.html") == -1) { return false; } Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; } } }
- 調査/開発用のAndroid端末としては ASUS ZenFone5 (Android 4.4.2)を使用しています。
5.残りの課題
まだ一部未解決のものがあります。
- Offline時にはトップページのツイッターボタンは非表示としているが表示されてしまう。
- 左上のロゴからトップに戻るとレイアウトが不完全になることがある。(初回使用時に発生しやすい。キャッシュが不完全の可能性がある)
0 件のコメント:
コメントを投稿