JAVAアプリをアンドロイドに移植 Android版のAozoraEpub3の制作

パソコンで動作していたアプリをアンドロイドへ移植したときの苦労した所を書いていく。ChatGPTを使って変換できるところは試したほうが早く作れた。

「次のコードAndroid向けに書き換えて」とChatGPTに指示すると、Androidでは動かない部分を書き換えてくれる。

## 画像の読み込み方が違う
アンドロイドにはImageIOがないのでBitmapFactoryを使わないといけない。そのためBufferedImage → BitmapFactoryに変更している。

パソコン用
```java

	/** ファイルから画像情報を生成 */
	static public ImageInfo getImageInfo(File imageFile) throws IOException
	{
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(imageFile));
		ImageInfo imageInfo = ImageInfo.getImageInfo(bis);
		bis.close();
		return imageInfo;
	}
	
	/** 画像ストリームから画像情報を生成
	 * @throws IOException */
	static public ImageInfo getImageInfo(InputStream is) throws IOException
	{
		ImageInfo imageInfo = null;
		ImageInputStream iis = ImageIO.createImageInputStream(is);
		Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
		if (readers.hasNext()) {
			ImageReader reader = readers.next();
			if (readers.hasNext() && reader.getClass().getName().endsWith("CLibPNGImageReader")) readers.next();
			reader.setInput(iis);
			String ext = reader.getFormatName();
			imageInfo = new ImageInfo(ext, reader.getWidth(0), reader.getHeight(0));
			reader.dispose();
		}
		return imageInfo;
	}
```
android用
```java
 /** Uri から画像情報を取得 (Android用)
     * @param context Androidの `Context`
     * @param imageUri 画像の `Uri`
     */
    public static ImageInfo getImageInfo(Context context, Uri imageUri) throws IOException {
        ContentResolver resolver = context.getContentResolver();
        InputStream is = resolver.openInputStream(imageUri);
        if (is == null) {
            throw new IOException("Failed to open InputStream from Uri");
        }
        return getImageInfo(is, getFileExtension(imageUri.toString()));
    }

    /** InputStream から画像情報を取得 */
    private static ImageInfo getImageInfo(InputStream is, String ext) throws IOException {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 画像のデコードはせず、サイズのみ取得
        BitmapFactory.decodeStream(is, null, options);
        is.close();
        return new ImageInfo(ext, options.outWidth, options.outHeight);
    }

    /** ファイル名やパスから拡張子を取得 */
    private static String getFileExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        if (lastDot == -1) return "";
        return filename.substring(lastDot + 1).toLowerCase();
    }
```
## テンプレートファイルへのアクセス方法が違う。
velocityを使ってテンプレートファイルを読み込んでHTMLを生成しているときに、テンプレートファイルを好きな場所に置けない。

```java
String jarPath ="";
        AozoraGaijiConverter gaijiconverter;
            gaijiconverter = new AozoraGaijiConverter(jarPath);

         gaijiconverter = new AozoraGaijiConverter(this);

	public AozoraGaijiConverter(String jarPath) throws IOException
	{
		//初期化
		//ファイルチェック取得 IVS優先
		this.loadChukiFile(new File(jarPath+"chuki_ivs.txt"), chukiUtfMap);
		this.loadChukiFile(new File(jarPath+"chuki_utf.txt"), chukiUtfMap);
		this.loadChukiFile(new File(jarPath+"chuki_alt.txt"), chukiAltMap);
	}

	public AozoraGaijiConverter(Context context) throws IOException
	{
		//初期化
		//ファイルチェック取得 IVS優先

		this.loadChukiFile(context.getAssets().open("chuki_ivs.txt"), chukiUtfMap);
		this.loadChukiFile(context.getAssets().open("chuki_utf.txt"), chukiUtfMap);
		this.loadChukiFile(context.getAssets().open("chuki_alt.txt"), chukiAltMap);
	}
````
```java		this.loadChukiFile(context.getAssets().open("chuki_ivs.txt"), chukiUtfMap);
		this.loadChukiFile(context.getAssets().open("chuki_utf.txt"), chukiUtfMap);
		this.loadChukiFile(context.getAssets().open("chuki_alt.txt"), chukiAltMap);

BufferedReader src = new BufferedReader(new InputStreamReader(context.getAssets().open("chuki_tag.txt"), StandardCharsets.UTF_8));
src = new BufferedReader(new InputStreamReader(context.getAssets().open("chuki_tag_suf.txt"), StandardCharsets.UTF_8));
src = new BufferedReader(new InputStreamReader(context.getAssets().open("replace.txt"), StandardCharsets.UTF_8));
```
        Epub3Writer writer = new Epub3Writer(this);
contextを渡すようにする


Velocity.mergeTemplateではファイルパスからテンプレートファイルを読み込むが、context.getAssets().openではinputstreamになるので
Velocity.evaluateを使って、ファイルパスではなくテンプレートファイルのstringを渡すように変える。
```java
Velocity.mergeTemplate(templatePath+OPS_PATH+CSS_PATH+TEXT_CSS_VM, "UTF-8", velocityContext, bw);


String template = IOUtils.toString(context.getAssets().open(templatePath+OPS_PATH+CSS_PATH+TEXT_CSS_VM), StandardCharsets.UTF_8);
Velocity.evaluate(velocityContext, bw, null, template);

String template = inputStreemToString(context.getAssets().open(templatePath+OPS_PATH+CSS_PATH+TEXT_CSS_VM), StandardCharsets.UTF_8);
Velocity.evaluate(velocityContext, bw, null, template);

			//Velocity.mergeTemplate(vmFilePath, "UTF-8", velocityContext, bw);
			String template = inputStreemToString(context.getAssets().open(vmFilePath), StandardCharsets.UTF_8);
			Velocity.evaluate(velocityContext, bw, null, template);

				//Velocity.mergeTemplate(templatePath+OPS_PATH+XHTML_PATH+COVER_VM, "UTF-8", velocityContext, bw);
				String template = inputStreemToString(context.getAssets().open(templatePath+OPS_PATH+XHTML_PATH+COVER_VM), StandardCharsets.UTF_8);
				Velocity.evaluate(velocityContext, bw, null, template);

		//Velocity.mergeTemplate(templatePath+OPS_PATH+PACKAGE_VM, "UTF-8", velocityContext, bw);
		String template = inputStreemToString(context.getAssets().open(templatePath+OPS_PATH+PACKAGE_VM), StandardCharsets.UTF_8);
		Velocity.evaluate(velocityContext, bw, null, template);

		//Velocity.mergeTemplate(templatePath+OPS_PATH+XHTML_PATH+XHTML_NAV_VM, "UTF-8", velocityContext, bw);
		template = inputStreemToString(context.getAssets().open(templatePath+OPS_PATH+XHTML_PATH+XHTML_NAV_VM), StandardCharsets.UTF_8);
		Velocity.evaluate(velocityContext, bw, null, template);

		//Velocity.mergeTemplate(templatePath+OPS_PATH+TOC_VM, "UTF-8", velocityContext, bw);
		template = inputStreemToString(context.getAssets().open(templatePath+OPS_PATH+TOC_VM), StandardCharsets.UTF_8);
		Velocity.evaluate(velocityContext, bw, null, template);

		//Velocity.mergeTemplate(this.templatePath+OPS_PATH+XHTML_PATH+XHTML_HEADER_VM, "UTF-8", velocityContext, bw);
		String template = inputStreemToString(context.getAssets().open(this.templatePath+OPS_PATH+XHTML_PATH+XHTML_HEADER_VM), StandardCharsets.UTF_8);
		Velocity.evaluate(velocityContext, bw, null, template);

		//Velocity.mergeTemplate(this.templatePath+OPS_PATH+XHTML_PATH+XHTML_FOOTER_VM, "UTF-8", velocityContext, bw);
		String template = inputStreemToString(context.getAssets().open(this.templatePath+OPS_PATH+XHTML_PATH+XHTML_FOOTER_VM), StandardCharsets.UTF_8);
		Velocity.evaluate(velocityContext, bw, null, template);

	public Epub3ImageWriter(Context context)
	{
		super(context);
	}
```
androidではassetにファイルを置き、assetmanagerで管理している。読み出すときもassetmanagerが必要になる。assetmanagerはmainactivityからcontextを渡す必要がある。
PCではjarファイルのパスを渡して相対パスでテンプレートファイルにアクセスしていた。

## 設定ファイルの書き方が違う
iniのpropertyで設定を管理していた。androidではpreferenceで管理される。

iniの場合はiniからGUIに設定を反映し、終了時にGUIからiniに保存していた。

## GUIのパーツが違う

同じアプリのパソコン版とアンドロイド版を比較すると書き方の違いが見えてくる。

パソコン版AozoraEpub3

https://github.com/kyukyunyorituryo/AozoraEpub3/

アンドロイド版AozoraEpub3

https://github.com/kyukyunyorituryo/AozoraEpub3-Android

https://play.google.com/store/apps/details?id=io.github.kyukyunyorituryo.aozoraepub3


https://youtube.com/shorts/B6i-MCUwq8o