diff --git a/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt b/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt index 999d0b9662..000a8aa2e0 100644 --- a/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt +++ b/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt @@ -8,6 +8,7 @@ import android.net.Uri import android.os.Bundle import android.provider.Settings import android.text.TextUtils +import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -70,15 +71,15 @@ abstract class BaseContainerFragment : BaseResourceFragment() { AdapterCourses.showRating(`object`, rating, timesRated, ratingBar) } } - fun getUrlsAndStartDownload( - lib: List, urls: ArrayList - ) { + + fun getUrlsAndStartDownload(lib: List, urls: ArrayList) { for (library in lib) { val url = Utilities.getUrl(library) if (!FileUtils.checkFileExist(url) && !TextUtils.isEmpty(url)) urls.add(url) } if (urls.isNotEmpty()) startDownload(urls) } + fun initRatingView(type: String?, id: String?, title: String?, listener: OnRatingChangeListener?) { timesRated = requireView().findViewById(R.id.times_rated) rating = requireView().findViewById(R.id.tv_rating) @@ -98,12 +99,14 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } } + override fun onAttach(context: Context) { super.onAttach(context) if (context is OnHomeItemClickListener) { homeItemClickListener = context } } + private fun openIntent(items: RealmMyLibrary, typeClass: Class<*>?) { val fileOpenIntent = Intent(activity, typeClass) if (items.resourceLocalAddress?.contains("ole/audio") == true || items.resourceLocalAddress?.contains("ole/video") == true) { @@ -115,6 +118,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } startActivity(fileOpenIntent) } + private fun openPdf(item: RealmMyLibrary) { val fileOpenIntent = Intent(activity, PDFReaderActivity::class.java) fileOpenIntent.putExtra("TOUCHED_FILE", item.id + "/" + item.resourceLocalAddress) @@ -123,31 +127,37 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } fun openResource(items: RealmMyLibrary) { - val matchingItems = mRealm.where(RealmMyLibrary::class.java) - .equalTo("resourceLocalAddress", items.resourceLocalAddress) - .findAll() - val anyOffline = matchingItems.any { it.isResourceOffline() } - if (anyOffline) { - val offlineItem = matchingItems.first { it.isResourceOffline()} - openFileType(offlineItem, "offline") - } else { - if (items.isResourceOffline()) { - openFileType(items, "offline") - } else if (FileUtils.getFileExtension(items.resourceLocalAddress) == "mp4") { - openFileType(items, "online") - } else { - val arrayList = ArrayList() - arrayList.add(Utilities.getUrl(items)) - startDownload(arrayList) - profileDbHandler.setResourceOpenCount(items, KEY_RESOURCE_DOWNLOAD) - } + val intent = Intent(context, WebViewActivity::class.java).apply { + putExtra("isLocalFile", true) + putExtra("RESOURCE_ID", "6f7b1d7a7c9c73e736b38657bc013556") // The _id from the library item } + startActivity(intent) +// val matchingItems = mRealm.where(RealmMyLibrary::class.java) +// .equalTo("resourceLocalAddress", items.resourceLocalAddress) +// .findAll() +// val anyOffline = matchingItems.any { it.isResourceOffline() } +// if (anyOffline) { +// val offlineItem = matchingItems.first { it.isResourceOffline()} +// openFileType(offlineItem, "offline") +// } else { +// if (items.isResourceOffline()) { +// openFileType(items, "offline") +// } else if (FileUtils.getFileExtension(items.resourceLocalAddress) == "mp4") { +// openFileType(items, "online") +// } else { +// val arrayList = ArrayList() +// arrayList.add(Utilities.getUrl(items)) +// startDownload(arrayList) +// profileDbHandler.setResourceOpenCount(items, KEY_RESOURCE_DOWNLOAD) +// } +// } } private fun checkFileExtension(items: RealmMyLibrary) { - val filenameArray = items.resourceLocalAddress?.split("\\.".toRegex())?.toTypedArray() - val extension = filenameArray?.get(filenameArray.size - 1) val mimetype = Utilities.getMimeType(items.resourceLocalAddress) + val extension = items.resourceLocalAddress + ?.substringAfterLast('.', "") + ?.lowercase() if (mimetype != null) { if (mimetype.contains("image")) { @@ -167,6 +177,9 @@ abstract class BaseContainerFragment : BaseResourceFragment() { "txt" -> { openIntent(items, TextFileViewerActivity::class.java) } + "html", "htm" -> { + openHtmlResource(items) + } "md" -> { openIntent(items, MarkdownViewerActivity::class.java) } @@ -233,6 +246,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { checkFileExtension(items) } } + open fun playVideo(videoType: String, items: RealmMyLibrary) { val intent = Intent(activity, VideoPlayerActivity::class.java) val bundle = Bundle() @@ -296,6 +310,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } } + fun setResourceButton(resources: List?, btnResources: Button) { if (resources.isNullOrEmpty()) { btnResources.visibility = View.GONE @@ -310,6 +325,15 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } + private fun openHtmlResource(items: RealmMyLibrary) { + val intent = Intent(activity, WebViewActivity::class.java).apply { + putExtra("TOUCHED_FILE", "${items.id}/${items.resourceLocalAddress}") + putExtra("title", items.title) + putExtra("isLocalFile", true) + } + startActivity(intent) + } + open fun handleBackPressed() { val fragmentManager = parentFragmentManager fragmentManager.popBackStack() diff --git a/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt.lite b/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt.lite index 385b2c0c8a..db66984e4c 100644 --- a/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt.lite +++ b/app/src/main/java/org/ole/planet/myplanet/base/BaseContainerFragment.kt.lite @@ -8,6 +8,7 @@ import android.net.Uri import android.os.Bundle //import android.provider.Settings import android.text.TextUtils +import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -70,15 +71,15 @@ abstract class BaseContainerFragment : BaseResourceFragment() { AdapterCourses.showRating(`object`, rating, timesRated, ratingBar) } } - fun getUrlsAndStartDownload( - lib: List, urls: ArrayList - ) { + + fun getUrlsAndStartDownload(lib: List, urls: ArrayList) { for (library in lib) { val url = Utilities.getUrl(library) if (!FileUtils.checkFileExist(url) && !TextUtils.isEmpty(url)) urls.add(url) } if (urls.isNotEmpty()) startDownload(urls) } + fun initRatingView(type: String?, id: String?, title: String?, listener: OnRatingChangeListener?) { timesRated = requireView().findViewById(R.id.times_rated) rating = requireView().findViewById(R.id.tv_rating) @@ -98,12 +99,14 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } } + override fun onAttach(context: Context) { super.onAttach(context) if (context is OnHomeItemClickListener) { homeItemClickListener = context } } + private fun openIntent(items: RealmMyLibrary, typeClass: Class<*>?) { val fileOpenIntent = Intent(activity, typeClass) if (items.resourceLocalAddress?.contains("ole/audio") == true || items.resourceLocalAddress?.contains("ole/video") == true) { @@ -115,6 +118,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } startActivity(fileOpenIntent) } + private fun openPdf(item: RealmMyLibrary) { val fileOpenIntent = Intent(activity, PDFReaderActivity::class.java) fileOpenIntent.putExtra("TOUCHED_FILE", item.id + "/" + item.resourceLocalAddress) @@ -145,9 +149,10 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } private fun checkFileExtension(items: RealmMyLibrary) { - val filenameArray = items.resourceLocalAddress?.split("\\.".toRegex())?.toTypedArray() - val extension = filenameArray?.get(filenameArray.size - 1) val mimetype = Utilities.getMimeType(items.resourceLocalAddress) + val extension = items.resourceLocalAddress + ?.substringAfterLast('.', "") + ?.lowercase() if (mimetype != null) { if (mimetype.contains("image")) { @@ -167,6 +172,9 @@ abstract class BaseContainerFragment : BaseResourceFragment() { "txt" -> { openIntent(items, TextFileViewerActivity::class.java) } + "html", "htm" -> { + openHtmlResource(items) + } "md" -> { openIntent(items, MarkdownViewerActivity::class.java) } @@ -233,6 +241,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { checkFileExtension(items) } } + open fun playVideo(videoType: String, items: RealmMyLibrary) { val intent = Intent(activity, VideoPlayerActivity::class.java) val bundle = Bundle() @@ -296,6 +305,7 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } } + fun setResourceButton(resources: List?, btnResources: Button) { if (resources.isNullOrEmpty()) { btnResources.visibility = View.GONE @@ -310,6 +320,15 @@ abstract class BaseContainerFragment : BaseResourceFragment() { } } + private fun openHtmlResource(items: RealmMyLibrary) { + val intent = Intent(activity, WebViewActivity::class.java).apply { + putExtra("TOUCHED_FILE", "${items.id}/${items.resourceLocalAddress}") + putExtra("title", items.title) + putExtra("isLocalFile", true) + } + startActivity(intent) + } + // open fun handleBackPressed() { // val fragmentManager = parentFragmentManager // fragmentManager.popBackStack() diff --git a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt index b360c194d3..55b5f1b7d6 100644 --- a/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt +++ b/app/src/main/java/org/ole/planet/myplanet/model/RealmMyLibrary.kt @@ -2,6 +2,7 @@ package org.ole.planet.myplanet.model import android.content.Context import android.content.SharedPreferences +import android.util.Log import com.google.gson.JsonArray import com.google.gson.JsonNull import com.google.gson.JsonObject @@ -20,6 +21,7 @@ import java.io.FileWriter import java.io.IOException import java.util.Calendar import java.util.Date +import java.util.UUID open class RealmMyLibrary : RealmObject() { @PrimaryKey @@ -63,6 +65,8 @@ open class RealmMyLibrary : RealmObject() { var courseId: String? = null var stepId: String? = null var isPrivate: Boolean = false + var attachments: RealmList? = null + fun serializeResource(): JsonObject { return JsonObject().apply { addProperty("_id", _id) @@ -258,6 +262,7 @@ open class RealmMyLibrary : RealmObject() { if (!mRealm.isInTransaction) { mRealm.beginTransaction() } + logLargeString("insertMyLibrary", doc.toString()) val resourceId = JsonUtils.getString("_id", doc) val settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) var resource = mRealm.where(RealmMyLibrary::class.java).equalTo("id", resourceId).findFirst() @@ -265,6 +270,7 @@ open class RealmMyLibrary : RealmObject() { resource = mRealm.createObject(RealmMyLibrary::class.java, resourceId) } resource?.apply { + setUserId(userId) _id = resourceId if (!stepId.isNullOrBlank()) { @@ -279,7 +285,25 @@ open class RealmMyLibrary : RealmObject() { description = JsonUtils.getString("description", doc) if (doc.has("_attachments")) { val attachments = doc["_attachments"].asJsonObject - attachments.entrySet().forEach { (key, _) -> + if (this.attachments == null) { + this.attachments = RealmList() + } + + attachments.entrySet().forEach { (key, attachmentValue) -> + val attachmentObj = attachmentValue.asJsonObject + + val realmAttachment = mRealm.createObject(RealmAttachment::class.java, UUID.randomUUID().toString()) + realmAttachment.apply { + name = key + contentType = attachmentObj.get("content_type")?.asString + length = attachmentObj.get("length")?.asLong ?: 0 + digest = attachmentObj.get("digest")?.asString + isStub = attachmentObj.get("stub")?.asBoolean == true + revpos = attachmentObj.get("revpos")?.asInt ?: 0 + } + + this.attachments?.add(realmAttachment) + if (key.indexOf("/") < 0) { resourceRemoteAddress = "${settings.getString("couchdbURL", "http://")}/resources/$resourceId/$key" resourceLocalAddress = key @@ -311,6 +335,8 @@ open class RealmMyLibrary : RealmObject() { isPrivate = JsonUtils.getBoolean("private", doc) setLanguages(JsonUtils.getJsonArray("languages", doc), this) } + + logLargeString("insertMyLibrary resource", resource.id.toString() + " " + resource.resourceRemoteAddress) mRealm.commitTransaction() val csvRow = arrayOf( @@ -402,12 +428,32 @@ open class RealmMyLibrary : RealmObject() { @JvmStatic fun getArrayList(libraries: List, type: String): Set { - return libraries.mapNotNull { if (type == "mediums") it.mediaType else it.language }.filterNot { it.isNullOrBlank() }.toSet() + return libraries.mapNotNull { if (type == "mediums") it.mediaType else it.language }.filterNot { it.isBlank() }.toSet() } @JvmStatic fun getSubjects(libraries: List): Set { return libraries.flatMap { it.subject ?: emptyList() }.toSet() } + + fun logLargeString(tag: String, content: String) { + if (content.length > 3000) { + Log.d(tag, content.substring(0, 3000)) + logLargeString(tag, content.substring(3000)) + } else { + Log.d(tag, content) + } + } } +} + +open class RealmAttachment : RealmObject() { + @PrimaryKey + var id: String? = null + var name: String? = null + var contentType: String? = null + var length: Long = 0 + var digest: String? = null + var isStub: Boolean = false + var revpos: Int = 0 } \ No newline at end of file diff --git a/app/src/main/java/org/ole/planet/myplanet/service/UploadToShelfService.kt b/app/src/main/java/org/ole/planet/myplanet/service/UploadToShelfService.kt index 652c0e1531..d944ccf5aa 100644 --- a/app/src/main/java/org/ole/planet/myplanet/service/UploadToShelfService.kt +++ b/app/src/main/java/org/ole/planet/myplanet/service/UploadToShelfService.kt @@ -43,7 +43,7 @@ class UploadToShelfService(context: Context) { for (model in userModels) { try { val password = sharedPreferences.getString("loginUserPassword", "") - val header = "Basic " + Base64.encodeToString(("${model.name}:${password}").toByteArray(), Base64.NO_WRAP) + val header = "Basic ${Base64.encodeToString(("${ model.name }:${ password }").toByteArray(), Base64.NO_WRAP)}" val res = apiInterface?.getJsonObject(header, "${replacedUrl(model)}/_users/org.couchdb.user:${model.name}")?.execute() if (res?.body() == null) { val obj = model.serialize() diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/resources/AdapterResource.kt b/app/src/main/java/org/ole/planet/myplanet/ui/resources/AdapterResource.kt index e38f8b7491..c0c8a59d48 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/resources/AdapterResource.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/resources/AdapterResource.kt @@ -2,6 +2,7 @@ package org.ole.planet.myplanet.ui.resources import android.content.Context import android.text.TextUtils +import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -19,6 +20,7 @@ import org.ole.planet.myplanet.callback.OnLibraryItemSelected import org.ole.planet.myplanet.callback.OnRatingChangeListener import org.ole.planet.myplanet.databinding.RowLibraryBinding import org.ole.planet.myplanet.model.RealmMyLibrary +import org.ole.planet.myplanet.model.RealmMyLibrary.Companion.logLargeString import org.ole.planet.myplanet.model.RealmTag import org.ole.planet.myplanet.model.RealmUserModel import org.ole.planet.myplanet.service.UserProfileDbHandler @@ -87,19 +89,67 @@ class AdapterResource(private val context: Context, private var libraryList: Lis } holder.rowLibraryBinding.tvDate.text = libraryList[position]?.createdDate?.let { formatDate(it, "MMM dd, yyyy") } displayTagCloud(holder.rowLibraryBinding.flexboxDrawable, position) - holder.itemView.setOnClickListener { openLibrary(libraryList[position]) } + holder.itemView.setOnClickListener { + openLibrary(libraryList[position]) + val libraryItem = realm.where(RealmMyLibrary::class.java) + .equalTo("id", libraryList[position]?.id) + .findFirst() + Log.d("AdapterResource", "onBindViewHolder: $libraryItem") + libraryItem?.let { item -> + val allDetails = """ + _id: ${item._id} + _rev: ${item._rev} + Title: ${item.title} + Author: ${item.author} + Year: ${item.year} + Description: ${item.description} + Language: ${item.language} + Publisher: ${item.publisher} + Link to License: ${item.linkToLicense} + Subjects: ${item.subject?.joinToString()} + Levels: ${item.level?.joinToString()} + Resource Type: ${item.resourceType} + Open With: ${item.openWith} + Media Type: ${item.mediaType} + Article Date: ${item.articleDate} + Resource For: ${item.resourceFor?.joinToString()} + Added By: ${item.addedBy} + Upload Date: ${item.uploadDate} + Created Date: ${item.createdDate} + Resource Remote Address: ${item.resourceRemoteAddress} + Resource Local Address: ${item.resourceLocalAddress} + Resource Offline: ${item.resourceOffline} + Resource ID: ${item.resourceId} + Downloaded Rev: ${item.downloadedRev} + Needs Optimization: ${item.needsOptimization} + Tags: ${item.tag?.joinToString()} + Languages: ${item.languages?.joinToString()} + Course ID: ${item.courseId} + Step ID: ${item.stepId} + Is Private: ${item.isPrivate} + User ID: ${item.userId?.joinToString()} + Filename: ${item.filename} + Translations Audio Path: ${item.translationAudioPath} + Average Rating: ${item.averageRating} + Times Rated: ${item.timesRated} + Sum: ${item.sum} + attachment: ${item.attachments?.joinToString()} + """.trimIndent() + + logLargeString("AdapterResource", "Full Library Item Details:\n$allDetails") + } + } userModel = UserProfileDbHandler(context).userModel if (libraryList[position]?.isResourceOffline() == true) { holder.rowLibraryBinding.ivDownloaded.visibility = View.INVISIBLE } else { holder.rowLibraryBinding.ivDownloaded.visibility = View.VISIBLE } - holder.rowLibraryBinding.ivDownloaded.contentDescription = - if (libraryList[position]?.isResourceOffline() == true) { - context.getString(R.string.view) - } else { - context.getString(R.string.download) - } + holder.rowLibraryBinding.ivDownloaded.contentDescription = if (libraryList[position]?.isResourceOffline() == true) { + context.getString(R.string.view) + } else { + context.getString(R.string.download) + } if (ratingMap.containsKey(libraryList[position]?.resourceId)) { val `object` = ratingMap[libraryList[position]?.resourceId] AdapterCourses.showRating(`object`, holder.rowLibraryBinding.rating, holder.rowLibraryBinding.timesRated, holder.rowLibraryBinding.ratingBar) @@ -109,8 +159,7 @@ class AdapterResource(private val context: Context, private var libraryList: Lis if (userModel?.isGuest() == false) { holder.rowLibraryBinding.checkbox.setOnClickListener { view: View -> - holder.rowLibraryBinding.checkbox.contentDescription = - context.getString(R.string.select_res_course, libraryList[position]?.title) + holder.rowLibraryBinding.checkbox.contentDescription = context.getString(R.string.select_res_course, libraryList[position]?.title) Utilities.handleCheck((view as CheckBox).isChecked, position, selectedItems, libraryList) if (listener != null) listener?.onSelectedListChange(selectedItems) } diff --git a/app/src/main/java/org/ole/planet/myplanet/ui/viewer/WebViewActivity.kt b/app/src/main/java/org/ole/planet/myplanet/ui/viewer/WebViewActivity.kt index 7dfbefc064..ee89b179c2 100644 --- a/app/src/main/java/org/ole/planet/myplanet/ui/viewer/WebViewActivity.kt +++ b/app/src/main/java/org/ole/planet/myplanet/ui/viewer/WebViewActivity.kt @@ -1,21 +1,42 @@ package org.ole.planet.myplanet.ui.viewer +import android.annotation.TargetApi import android.graphics.Bitmap import android.net.Uri +import android.os.Build import android.os.Bundle import android.text.TextUtils +import android.util.Log import android.view.View import android.webkit.CookieManager import android.webkit.WebChromeClient +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebSettings import android.webkit.WebView import android.webkit.WebViewClient import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.FileProvider +import androidx.lifecycle.lifecycleScope +import io.realm.Realm +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.ole.planet.myplanet.MainApplication import org.ole.planet.myplanet.databinding.ActivityWebViewBinding +import org.ole.planet.myplanet.model.RealmAttachment +import org.ole.planet.myplanet.model.RealmMyLibrary +import org.ole.planet.myplanet.utilities.Utilities +import java.io.File +import java.net.HttpURLConnection +import java.net.URL class WebViewActivity : AppCompatActivity() { private lateinit var activityWebViewBinding: ActivityWebViewBinding private var fromDeepLink = false private lateinit var link: String + private var isLocalFile = false + var resourceId: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -25,22 +46,410 @@ class WebViewActivity : AppCompatActivity() { fromDeepLink = !TextUtils.isEmpty(dataFromDeepLink) val title: String? = intent.getStringExtra("title") link = intent.getStringExtra("link") ?: "" + isLocalFile = intent.getBooleanExtra("isLocalFile", false) + + if (isLocalFile) { + resourceId = intent.getStringExtra("RESOURCE_ID") + Log.d("okuro", "$resourceId") + if (!resourceId.isNullOrEmpty()) { + lifecycleScope.launch { + loadLocalHtmlResource(resourceId) + } + } + } + + Log.d("WebViewActivity", "onCreate: $link, isLocalFile: $isLocalFile") clearCookie() + if (!TextUtils.isEmpty(title)) { activityWebViewBinding.contentWebView.webTitle.text = title } + activityWebViewBinding.contentWebView.pBar.max = 100 activityWebViewBinding.contentWebView.pBar.progress = 0 setListeners() - activityWebViewBinding.contentWebView.wv.settings.javaScriptEnabled = true - activityWebViewBinding.contentWebView.wv.settings.javaScriptCanOpenWindowsAutomatically = true - activityWebViewBinding.contentWebView.wv.loadUrl(link) + activityWebViewBinding.contentWebView.wv.settings.apply { + javaScriptEnabled = true + domStorageEnabled = true + javaScriptCanOpenWindowsAutomatically = true + mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW + setSupportZoom(true) + builtInZoomControls = true + } + +// if (isLocalFile) { +// val touchedFile = intent.getStringExtra("TOUCHED_FILE") +// if (!touchedFile.isNullOrEmpty()) { +// val localFilePath = File(MainApplication.context.getExternalFilesDir(null), touchedFile).absolutePath +// activityWebViewBinding.contentWebView.wv.loadUrl("file://$localFilePath") +// } +// } else { +// val headers = mapOf("Authorization" to Utilities.header) +// activityWebViewBinding.contentWebView.wv.loadUrl(link, headers) +// } activityWebViewBinding.contentWebView.finish.setOnClickListener { finish() } setWebClient() } + private suspend fun loadLocalHtmlResource(resourceId: String?) { + withContext(Dispatchers.IO) { + try { + withContext(Dispatchers.Main) { + activityWebViewBinding.contentWebView.pBar.visibility = View.VISIBLE + } + + // Create resource directory structure + val gamesDir = File(filesDir, "games") + val resourceDir = File(gamesDir, resourceId) + resourceDir.mkdirs() + + // Create necessary directories + File(resourceDir, "js").mkdirs() + File(resourceDir, "style").mkdirs() + File(resourceDir, "style/fonts").mkdirs() + File(resourceDir, "meta").mkdirs() + + // Copy all required files + copyJsFiles(resourceDir) + copyCssFiles(resourceDir) + copyFontFiles(resourceDir) + copyMetaFiles(resourceDir) + + // Get HTML content and write to file + val htmlContent = retrieveHtmlContentFromRealm(resourceId) + if (htmlContent != null) { + val htmlFile = File(resourceDir, "index.html") + htmlFile.writeText(htmlContent) + + withContext(Dispatchers.Main) { + try { + // Load the file directly using file:// protocol + val fileUrl = "file://${htmlFile.absolutePath}" + Log.d("WebViewActivity", "Loading URL: $fileUrl") + + activityWebViewBinding.contentWebView.wv.settings.apply { + allowFileAccess = true + allowFileAccessFromFileURLs = true + allowUniversalAccessFromFileURLs = true + domStorageEnabled = true + javaScriptEnabled = true + } + + activityWebViewBinding.contentWebView.wv.loadUrl(fileUrl) + } catch (e: Exception) { + Log.e("WebViewActivity", "Error loading HTML file", e) + e.printStackTrace() + } + } + } + + } catch (e: Exception) { + Log.e("WebViewActivity", "Error in loadLocalHtmlResource", e) + } finally { + withContext(Dispatchers.Main) { + activityWebViewBinding.contentWebView.pBar.visibility = View.GONE + } + } + } + } + + private suspend fun copyJsFiles(resourceDir: File) { + withContext(Dispatchers.IO) { + val jsDir = File(resourceDir, "js") + copyFiles(resourceDir, "js/", "application/javascript", jsDir) + } + } + + private suspend fun copyCssFiles(resourceDir: File) { + withContext(Dispatchers.IO) { + val styleDir = File(resourceDir, "style") + copyFiles(resourceDir, "style/", "text/css", styleDir) + } + } + + private suspend fun copyFontFiles(resourceDir: File) { + withContext(Dispatchers.IO) { + val fontsDir = File(resourceDir, "style/fonts") + val fontTypes = listOf( + "application/font-woff", + "application/vnd.ms-fontobject", + "image/svg+xml" + ) + fontTypes.forEach { type -> + copyFiles(resourceDir, "style/fonts/", type, fontsDir) + } + } + } + + private suspend fun copyMetaFiles(resourceDir: File) { + withContext(Dispatchers.IO) { + val metaDir = File(resourceDir, "meta") + copyFiles(resourceDir, "meta/", "image/png", metaDir) + } + } + + private suspend fun copyFiles(resourceDir: File, prefix: String, contentType: String, destDir: File) { + val realm = Realm.getDefaultInstance() + try { + val libraryItem = realm.where(RealmMyLibrary::class.java) + .equalTo("_id", resourceId) + .findFirst() + + val attachments = libraryItem?.attachments?.filter { + it.name?.startsWith(prefix) == true && it.contentType == contentType + } + + attachments?.forEach { attachment -> + val fileName = attachment.name?.substringAfterLast("/") + if (fileName != null) { + val destFile = File(destDir, fileName) + + val fullAttachment = realm.where(RealmAttachment::class.java) + .equalTo("id", attachment.id) + .findFirst() + + fullAttachment?.let { + val fileContent = readAttachmentContent(it) + if (fileContent != null) { + destFile.writeText(fileContent) + Log.d("WebViewActivity", "Copied file: ${attachment.name}") + } + } + } + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error copying files for prefix $prefix", e) + } finally { + realm.close() + } + } + + private suspend fun retrieveHtmlContentFromRealm(resourceId: String?): String? { + return withContext(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + try { + val libraryItem = realm.where(RealmMyLibrary::class.java) + .equalTo("_id", resourceId) + .findFirst() + + val htmlAttachment = libraryItem?.attachments?.firstOrNull { + it.name == "index.html" + } + + Log.d("okuro", "htmlAttachment: $htmlAttachment") + + htmlAttachment?.let { attachment -> + realm.where(RealmAttachment::class.java) + .equalTo("id", attachment.id) + .findFirst() + ?.let { fullAttachment -> + readAttachmentContent(fullAttachment) + } + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error retrieving HTML from Realm", e) + null + } finally { + realm.close() + } + } + } + + private suspend fun readAttachmentContent(attachment: RealmAttachment): String? { + return withContext(Dispatchers.IO) { + val file = File(MainApplication.context.getExternalFilesDir(null), attachment.name) + Log.d("WebViewActivity", "Checking file: ${file.absolutePath}, exists: ${file.exists()}") + if (file.exists()) { + file.readText() + } else { + null + } + } + } + + private suspend fun ensureResourceDownloaded(resourceId: String?) { + if (resourceId == null) return + + withContext(Dispatchers.IO) { + val resourceDir = createResourceDirectoryStructure(resourceId) + val realm = Realm.getDefaultInstance() + + try { + Log.d("WebViewActivity", "Looking for resource with ID: $resourceId") + val libraryItem = realm.where(RealmMyLibrary::class.java) + .equalTo("_id", resourceId) + .findFirst() + + if (libraryItem == null) { + Log.e("WebViewActivity", "Library item not found for ID: $resourceId") + return@withContext + } + + Log.d("WebViewActivity", "Found library item with ${libraryItem.attachments?.size} attachments") + + libraryItem.attachments?.forEach { attachment -> + if (attachment.isStub) { + val destFile = File(resourceDir, attachment.name) + if (!destFile.exists()) { + Log.d("WebViewActivity", "Attempting to download: ${attachment.name}") + try { + val content = downloadAttachmentContent(attachment) + if (content != null) { + destFile.parentFile?.mkdirs() + destFile.writeText(content) + Log.d("WebViewActivity", "Successfully saved: ${attachment.name}") + } else { + Log.e("WebViewActivity", "Failed to download: ${attachment.name}") + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error downloading ${attachment.name}", e) + } + } else { + Log.d("WebViewActivity", "File already exists: ${attachment.name}") + } + } + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error in ensureResourceDownloaded", e) + } finally { + realm.close() + } + } + } + + private suspend fun downloadAttachmentContent(attachment: RealmAttachment): String? { + return withContext(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + try { + val libraryItem = realm.where(RealmMyLibrary::class.java) + .equalTo("_id", resourceId) + .findFirst() + + Log.d("WebViewActivity", "Found library item: ${libraryItem?._id}") + Log.d("WebViewActivity", "Remote address: ${libraryItem?.resourceRemoteAddress}") + + val remoteAddress = libraryItem?.resourceRemoteAddress + if (remoteAddress == null) { + Log.e("WebViewActivity", "Remote address not found for resource") + return@withContext null + } + + val baseUrl = if (remoteAddress.contains("@")) { + val urlParts = remoteAddress.split("@") + val auth = urlParts[0] + val restOfUrl = urlParts[1] + "${Utilities.getUrl()}$restOfUrl" + } else { + remoteAddress + }.substringBeforeLast("/") + + val attachmentUrl = "$baseUrl/${attachment.name}" + Log.d("WebViewActivity", "Attempting to download from: $attachmentUrl") + + try { + val url = URL(attachmentUrl) + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + if (remoteAddress.contains("@")) { + val auth = remoteAddress.split("@")[0].split("://")[1] + val base64Auth = android.util.Base64.encodeToString( + auth.toByteArray(), + android.util.Base64.NO_WRAP + ) + connection.setRequestProperty("Authorization", "Basic $base64Auth") + } + + try { + val responseCode = connection.responseCode + Log.d("WebViewActivity", "Response code for ${attachment.name}: $responseCode") + + if (responseCode == HttpURLConnection.HTTP_OK) { + val content = connection.inputStream.bufferedReader().use { it.readText() } + + val localFile = File(MainApplication.context.getExternalFilesDir(null), attachment.name) + localFile.parentFile?.mkdirs() + localFile.writeText(content) + + Log.d("WebViewActivity", "Successfully downloaded ${attachment.name}") + return@withContext content + } else { + val errorStream = connection.errorStream?.bufferedReader()?.use { it.readText() } + Log.e("WebViewActivity", "Failed to download attachment: HTTP $responseCode") + Log.e("WebViewActivity", "Error response: $errorStream") + return@withContext null + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Connection error for ${attachment.name}", e) + return@withContext null + } finally { + connection.disconnect() + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error processing URL for ${attachment.name}", e) + return@withContext null + } + } catch (e: Exception) { + Log.e("WebViewActivity", "Error in Realm query", e) + return@withContext null + } finally { + realm.close() + } + } + } + + private fun createResourceDirectoryStructure(resourceId: String): File { + val gamesDir = File(filesDir, "games") + gamesDir.mkdirs() + + val resourceDir = File(gamesDir, resourceId) + resourceDir.mkdirs() + + File(resourceDir, "js").mkdirs() + File(resourceDir, "style").mkdirs() + File(resourceDir, "style/fonts").mkdirs() + File(resourceDir, "meta").mkdirs() + + return resourceDir + } + private fun setWebClient() { activityWebViewBinding.contentWebView.wv.webViewClient = object : WebViewClient() { + override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? { + if (request == null) return null + + val url = request.url.toString() + Log.d("WebViewActivity", "Intercepting request: $url") + + val gamesDir = File(filesDir, "games") + val resourceDir = File(gamesDir, resourceId) + + try { + // Extract the path from the URL + val path = url.substringAfter("file://") + val file = File(path) + + if (file.exists()) { + val mimeType = when { + url.endsWith(".js") -> "application/javascript" + url.endsWith(".css") -> "text/css" + url.endsWith(".woff") -> "application/font-woff" + url.endsWith(".eot") -> "application/vnd.ms-fontobject" + url.endsWith(".svg") -> "image/svg+xml" + url.endsWith(".png") -> "image/png" + else -> "text/plain" + } + return WebResourceResponse(mimeType, "UTF-8", file.inputStream()) + } + + Log.d("WebViewActivity", "Resource not found: $url") + } catch (e: Exception) { + Log.e("WebViewActivity", "Error intercepting request: $url", e) + } + + return super.shouldInterceptRequest(view, request) + } + override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) if (url.endsWith("/eng/")) { @@ -50,8 +459,10 @@ class WebViewActivity : AppCompatActivity() { activityWebViewBinding.contentWebView.webSource.text = i.host } - override fun onPageFinished(view: WebView, url: String) { + override fun onPageFinished(view: WebView?, url: String?) { super.onPageFinished(view, url) + Log.d("WebViewActivity", "Page finished loading: $url") + activityWebViewBinding.contentWebView.pBar.visibility = View.GONE } } } @@ -62,7 +473,6 @@ class WebViewActivity : AppCompatActivity() { cookieManager.flush() } - private fun setListeners() { activityWebViewBinding.contentWebView.wv.webChromeClient = object : WebChromeClient() { override fun onProgressChanged(view: WebView, newProgress: Int) { diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml index 8248868e52..b8ff724e09 100644 --- a/app/src/main/res/xml/file_paths.xml +++ b/app/src/main/res/xml/file_paths.xml @@ -1,4 +1,5 @@ + \ No newline at end of file