package jp.juggler.subwaytooter.actmain import android.net.Uri import android.os.Process import android.util.JsonReader import android.view.WindowManager import androidx.annotation.WorkerThread import jp.juggler.subwaytooter.ActMain import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.appsetting.AppDataExporter import jp.juggler.subwaytooter.column.Column import jp.juggler.subwaytooter.notification.setImportProtector import jp.juggler.util.coroutine.launchProgress import jp.juggler.util.coroutine.runOnMainLooper import jp.juggler.util.log.LogCategory import jp.juggler.util.log.showToast import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.io.InputStreamReader import java.util.zip.ZipInputStream private val log = LogCategory("ActMainImportAppData") @WorkerThread fun ActMain.importAppData(uri: Uri) { // remove all columns phoneOnly { env -> env.pager.adapter = null } appState.editColumnList(save = false) { list -> list.forEach { it.dispose() } list.clear() } phoneTab( { env -> env.pager.adapter = env.pagerAdapter }, { env -> resizeColumnWidth(env) } ) updateColumnStrip() launchProgress( "importing app data", doInBackground = { progress -> fun setProgressMessage(sv: String) = runOnMainLooper { progress.setMessageEx(sv) } setProgressMessage("import data to local storage...") // アプリ内領域に一時ファイルを作ってコピーする val cacheDir = cacheDir cacheDir.mkdir() val file = File(cacheDir, "SubwayTooter.${Process.myPid()}.${Process.myTid()}.tmp") val copyBytes = contentResolver.openInputStream(uri)?.use { inStream -> FileOutputStream(file).use { outStream -> inStream.copyTo(outStream) } } if (copyBytes == null) { showToast(true, "openInputStream failed.") return@launchProgress null } // 通知サービスを止める setProgressMessage("syncing notification poller…") setImportProtector(this@importAppData, true) // データを読み込む setProgressMessage("reading app data...") var newColumnList: ArrayList? = null var zipEntryCount = 0 try { ZipInputStream(FileInputStream(file)).use { zipStream -> while (true) { val entry = zipStream.nextEntry ?: break ++zipEntryCount try { // val entryName = entry.name if (entryName.endsWith(".json")) { newColumnList = AppDataExporter.decodeAppData( this@importAppData, JsonReader(InputStreamReader(zipStream, "UTF-8")) ) continue } if (AppDataExporter.restoreBackgroundImage( this@importAppData, newColumnList, zipStream, entryName ) ) { continue } } finally { zipStream.closeEntry() } } } } catch (ex: Throwable) { if (zipEntryCount != 0) { log.e(ex, "importAppData failed.") showToast(ex, "importAppData failed.") } } // zipではなかった場合、zipEntryがない状態になる。例外はPH-1では出なかったが、出ても問題ないようにする。 if (zipEntryCount == 0) { InputStreamReader(FileInputStream(file), "UTF-8").use { inStream -> newColumnList = AppDataExporter.decodeAppData(this@importAppData, JsonReader(inStream)) } } newColumnList }, afterProc = { // cancelled. if (it == null) return@launchProgress try { phoneOnly { env -> env.pager.adapter = null } appState.editColumnList { list -> list.clear() list.addAll(it) } phoneTab( { env -> env.pager.adapter = env.pagerAdapter }, { env -> resizeColumnWidth(env) } ) updateColumnStrip() } finally { // 通知サービスをリスタート setImportProtector(this@importAppData, false) } showToast(true, R.string.import_completed_please_restart_app) finish() }, preProc = { window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) }, postProc = { window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } ) }