Hatena::ブログ(Diary)

hishidaのblog このページをアンテナに追加 RSSフィード

プロフィール

hishida

hishida

EB series support page 管理人 ブログ

2017-04-21

[] 読書尚友 2.0.0

読書尚友に大きめの改良を行なったので、メジャーバージョンアップとみなして2.0.0とした。

f:id:hishida:20170421172734p:image

フォントIPA明朝から源ノ明朝に変えたので、電子書籍の文字が非常に美しくなった。またLatin文字もNoto Serifベースなので、英文でも同じフォントで対応できるのもいい。花園明朝Bを搭載したのは、サロゲートペアに対応したのでUnicode CJK Ext.B,C,D,Eを表示させるためである。ただし、フォントを2つ内蔵したので、apkのサイズが肥大化してしまい(約60MB)、残り領域が少ないとインストールできない可能性がある。Unicode拡張領域を使用する文書は滅多にないので、オプションダウンロードさせる仕組みにしたほうがいいかもしれない。(追記:2.0.1で花園明朝Bはオプションダウンロードにした)

PDFは目次に対応し、正式版とした。ただ階層目次の開閉には対応していないので、フラットに全目次が表示される。このあたりはまだ工夫の余地がある。

2017-04-15

[] android用OPDSクライアント公開

前回のブログ( 読書尚友のPDFサポート 他 - hishidaのblog )で検討中と書いたandroid用OPDSクライアントがそこそこ動くようになったので、Google Playで公開した。とりあえずカタログのナビゲーション機能を実装してみたが、次の段階ではOpenSearchに対応してみたい。

OPDS Viewer - Google Play の Android アプリ

f:id:hishida:20170415183054p:image

f:id:hishida:20170415183051p:image

初期状態では次のOPDSカタログを登録している。

Project Gutenberg http://m.gutenberg.org/?format=opds
Feedbooks http://www.feedbooks.com/publicdomain/catalog.atom
BookServer(Internet Archive) http://bookserver.archive.org/catalog/
Manybooks http://manybooks.net/opds/
Smashwordshttps://smashwords.com/atom
青空文庫 OPDS http://aozora.textlive.net/catalog.opds
台湾 中華電子佛典協會 漢文大蔵経 http://www.cbeta.org/opds/
達人出版会 http://tatsu-zine.com/catalogs.opds
O'Reilly Japan https://www.oreilly.co.jp/ebook/catalogs.opds

これ以外に、有名どころで O’Reilly Media(https://opds.oreilly.com/opds/)も動作を確認したが、リンク先が購買しかないので、初期登録からは外した。

日本の出版サイトでは技術評論社がOPDSを提供しているが(http://gihyo.jp/dp/catalogs.opds)、いろいろと技術的な問題があって初期登録からは外すことにした。何が問題かというと、feedの作りが悪いのか、Abdroidの標準のXmlPullParserではパースで文法エラーになる。SAX Parserだと読み込みに成功した。パーサーによる相性問題が他にもあるかもしれないので、設定でXmlPullParserとSAX Parserを選択できるようにしてみた。

また、技術評論社のOPDSには新刊(http://gihyo.jp/dp/new.opds)と既刊書(http://gihyo.jp/dp/all.opds)があり、既刊書の方のOPDSが、1600冊以上のentryを一度に返してくる作りになっている。スマートフォンでは長考状態になって全く実用に耐えない。件数が多い場合には100件ずつぐらいページ単位で返すような設計にすべきだと思う。

海外の有名な読書リーダー(Moon+ Reader、Aldiko等)では大抵はOPDSブラウズ機能を内蔵している。日本ではOPDS自体が普及していないこともあり、青空文庫専用ビューアが普及している。

正直OPDSクライアントのニーズがあるかどうかわからないが、個人的にはOPDSの勉強にはなった。

読書尚友にもライブラリ機能としてOPDSブラウズを組み込むことを予定している。

2017-04-07

[][]読書尚友のPDFサポート 他

PDF対応について

「読書尚友のPDF対応を開発中」という記事を2015年8月に載せてから、1年半も経ってしまった。

2015-08-26 - hishidaのblog

おさらいすると、Android用のPDFライブラリmuPDFをはじめGPLのものが多く、ソースの公開義務が生じるので、なかなか使用しづらい。このため、Android 用の書籍ビューアの中には、PDFビューアの部分だけプラグインにしているものもある(MHE Novel Viewer、Perfect Viewer等)。

その後、Android 5.0 Lollipop からPdfRendererという機能が標準で備わったが、これはページ毎にImageViewにビットマップで表示するもので、拡大縮小したときにスケールに合わせてレンダリングしなおさないと文字がギザギザになる等、あまり使いやすくない。またLollipop以上でないと使用できないので、kitkat以下を切り捨てることになり、制限がきつい。PdfRendererを使用した読書尚友のPDF対応は2015年時点でできていたのだが、上記の理由で公開していなかった。

ところが最近、Apache2.0ライセンスPDFライブラリで使用できそうなものを見つけた。

com.github.barteksc.pdfviewer.PDFView

GitHub - barteksc/AndroidPdfViewer: Android view for displaying PDFs rendered with PdfiumAndroid

スワイプによる拡大縮小、ダブルタップによる拡大もできるし、アンチエイリアスも効くので、PDFの表示 だけで編集をしないのなら、十分に実用になる。何より、Android3.0以降で使用できるので、大半のユーザが恩恵を得られる。

というわけで、読書尚友1.54でPDFビューアを実装してみた。apkのサイズが10kb→28kbと大きくなってしまったが、それほど問題にならないと思う。今の所、有料版だけの機能だが、free版にも入れるかどうかは今後検討したい。

この後の作業としては、PDF関連の機能の追加 (目次とメタ情報の表示機能)を予定している。

f:id:hishida:20170407160049p:image

書籍一覧のカード型UIへの変更とブクログサポート

他に最近の改良としては、書籍一覧のUIをCardViewを使用してカード型にした(Androidアプリの流行としては数年遅れだが)。RecyclerViewの導入も試してみたが、ViewHolderモデルのListViewと速度的な差はないので、結局ListViewのままとした。RecyclerViewでないとできないようなUI(スワイプによる削除や項目の移動など)が必要なければ、無理にRecyclerViewに変える必要はないと思う。

f:id:hishida:20170407160100p:image

また、メニューボタンにブクログへのリンクを追加してみた。独自の感想投稿機能を作るよりも、青空文庫で正式に使用されている感想投稿サイトをサポートしたほうがいいと判断した。

OPDSの検討開始

次の段階として、OPDSをサポートしてみようかと思っている。

JEPA|日本電子出版協会 OPDSとは?

OPDSを実装しておけば、Project Gutenbergなども接続できるようになる。そうなると英文のePubの表示をきちんとしないといけないが(せめて英文の行端揃えは必須)、時間をかければできていくのではないかと思う。

また、有志による青空文庫のOPDSも稼働しているようだ。

20 | 2月 | 2011 | 潮流工房

継続してサポートされる保証はないが、とても有意義な試みだ。

OPDSクライアントとして単独のアプリにするか、読書尚友に組み込むかはこれから検討する。

2017-03-21

[][] 読書尚友とEBPocket for Androidをsplit-screenに対応させた

Android7.0 Nougatからsplit-screenの機能が加わっているが、読書尚友とEBPocket for Androidをsplit-screenに対応させてみた。

といっても日常的に使用しているZenfone 3 laserにはまだAndroid7.0アップデートが来ないので、エミュレータでの動作確認になる。

(あまりアップデートが遅れるようだと、初めからAndroid7.0が搭載された格安SIMフリーのnova liteあたりに買い換えたほうがいいかもしれない)

実はAndroid 7.0 に対応していないアプリでもsplit-screenは使用できるが、"app may not work with split-screen."というメッセージが表示されてしまう。

Android 7.0 Split-screen対応

マルチ ウィンドウのサポート | Android Developers

Split-screenにする要件は、

  • 画面のサイズが動的に変更されても画面のパーツが正常に表示されること。読書尚友もEBPocketも画面の回転に対応しているので、これはクリアしている。
  • targetSdkVersionを24(Android7.0)以上にする
  • manifestsでapplicationかactivityに、android:resizeableActivity="true"を記述する。

エミュレータでの実行結果は次の通り。

f:id:hishida:20170321174708p:image

f:id:hishida:20170321174704p:image

縦横でEBPokcetのレイアウトが変わっていることがわかる。読書尚友で単語を選択してEBPocketでクリップボード検索で辞書を引くこともできる。これはEBシリーズ全体でやりたかったことのゴールに近い。

さてここで一つ問題があり、targetSdkVersionをAPI24(Android7.0)以上にするということは、API23(Android 6.0)で導入された新しいパーミッションの考え方に対応しないといけないということ。

どちらかというと、こちらの作業のほうが大変だった。

Android 6.0 パーミッション対応

実行時のパーミッション リクエスト | Android Developers

Android 5.x以前のパーミッションの考えかたは、アプリインストール時に一括で許可を与えるものだったが、Android6.0からは、パーミッションを使用するときに個別に許可・不許可できるようになった。

例えば、「カメラは許可するが位置情報の使用は許可しない」とかを選択できるようになった。

読書尚友、EBPocketの場合は、WRITE_EXTERNAL_STORAGEのパーミッションが必要になる。

パーミッションがあるかどうかを確認し、ない場合は要求するコードは次の通り。

//	権限があるかどうか確認
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
	// Should we show an explanation?
	if (ActivityCompat.shouldShowRequestPermissionRationale(this,
			Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
		// 説明が必要な場合。EBPocketの場合はパーミッションが必須なので要求
		ActivityCompat.requestPermissions(this, new String[]{
				Manifest.permission.WRITE_EXTERNAL_STORAGE
		}, REQCODE_PERMISSION);
	} else {
		// 説明が不要な場合。パーミッション要求する
		ActivityCompat.requestPermissions(this, new String[]{
				Manifest.permission.WRITE_EXTERNAL_STORAGE
		}, REQCODE_PERMISSION);
	}
	return;
}

パーミッション要求の結果はコールバックされる。

/**
 *
 * @param requestCode
 * @param permissions
 * @param grantResults
 */
@Override
public void onRequestPermissionsResult(int requestCode, String permissions, int grantResults) {
	switch (requestCode) {
		case REQCODE_PERMISSION: {
			if (grantResults.length > 0
					&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
				// パーミッションの取得に成功した。
				// パーミッションが必要な処理をここに書く

			} else {
				// パーミッションの取得に失敗した

			}
		}
	}
}

2017-03-09

[][] C配列Objective-Cオブジェクトを保存するのは危険?

やっと安定したと思っていたEBPocket for iOSだが、複合検索で外字を選択すると異常終了するという報告をいただいた。

EBPocket / EBWin サポート掲示板

エミュレータで調べたところ、32bit OSでは問題が起きず、64bit OSだけ異常終了するらしい。NSStringの文字列がいつの間にかautoreleaseされて不正参照になっていることまでわかったが、原因がわかるまでしばらく時間がかかった。

結論的には、C配列にNSString*のオブジェクト参照を入れていたためだった。

問題の個所はこんな感じで、NSString*の配列m_keywordをクラス変数として静的に確保していた。

#define	MAX_CPLX_GRP	10
@interface ComplexSearchViewController : UIViewController 
{
	//	省略

	NSString*m_keyword[MAX_CPLX_GRP];
}

クラスオブジェクト生存中は、クラス変数のm_keyword[]に代入した文字列生存するものだとなんとなく思いこんでいた。

Objective-Cのメモリ管理の基本であるalloc/init/retain/releaseなどについては、一応理解しています)

だがよく考えてみれば、m_keyword[]は単なるC配列なので、代入してもNSStringの参照カウントは増えない。これではどこかのタイミング(関数の出口など)でautoreleaseされるのは当然だ。

そこで次のようにC配列をやめてNSMutableArrayにしてみた。NSMutableArrayなら代入すれば参照カウントがインクリメントされてautoreleaseされなくなるのではないか。

NSMutableArrayでは C配列と同様に 変数名[添字] の形で代入や参照が書けるので、ソースの修正は最小限にできる(本当はreplaceObjectAtIndex:withObject:みたいな長ったらしい名前のメソッドがある。[]はいわゆるシンタックスシュガー)。

@interface ComplexSearchViewController : UIViewController 
{
	//	省略

	NSMutableArray*m_keyword;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
        // Custom initialization
        m_keyword = [[NSMutableArray alloc]initWithCapacity:MAX_CPLX_GRP];
    }
    return self;
}

- (void)dealloc {
	//	省略
    [m_keyword release];
	
    [super dealloc];
}

案の定、これで落ちなくなった。万歳。

Objective-Cはメモリ管理に気を使う。

今はiOS向けの新規案件は、より進化した言語であるSwiftの利用が増えているのではないだろうか。私もこれから何かiOS向けに書くとすれば、Swiftを選ぶ。読書尚友のiOS版をSwiftで作ってみてもいいかもしれない。