From a67f99fcf210792da5e028570efbad61407aeab2 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Tue, 4 Jul 2017 17:22:36 -0400 Subject: Dev (#21) - fix search and file chooser * Fix up search and add file chooser * Fix repeating file chooser --- app/src/main/assets/.gitignore | 3 +- app/src/main/assets/js/search_query.js | 10 ++++ .../kotlin/com/pitchedapps/frost/MainActivity.kt | 20 +++++++- .../com/pitchedapps/frost/WebOverlayActivity.kt | 23 ++++++++- .../com/pitchedapps/frost/contracts/FileChooser.kt | 56 ++++++++++++++++++++++ .../com/pitchedapps/frost/contracts/WebContract.kt | 8 ++++ .../com/pitchedapps/frost/web/FrostChromeClient.kt | 16 +++---- .../pitchedapps/frost/web/FrostWebViewClient.kt | 21 +++++--- .../pitchedapps/frost/web/FrostWebViewSearch.kt | 16 ++++--- gradle.properties | 2 +- 10 files changed, 150 insertions(+), 25 deletions(-) create mode 100644 app/src/main/assets/js/search_query.js create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/contracts/WebContract.kt diff --git a/app/src/main/assets/.gitignore b/app/src/main/assets/.gitignore index 62c89355..0d3aee97 100644 --- a/app/src/main/assets/.gitignore +++ b/app/src/main/assets/.gitignore @@ -1 +1,2 @@ -.idea/ \ No newline at end of file +.idea/ +js/search_query.min.js diff --git a/app/src/main/assets/js/search_query.js b/app/src/main/assets/js/search_query.js new file mode 100644 index 00000000..806519de --- /dev/null +++ b/app/src/main/assets/js/search_query.js @@ -0,0 +1,10 @@ +var e = document.getElementById('main-search-input'); +if (e) { + e.value = '$input'; + var n = new Event('input', { + bubbles: !0, + cancelable: !0 + }); + e.dispatchEvent(n); + e.dispatchEvent(new Event('focus')); +} else console.log('Input field not found') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index d52c12ac..b2dc676c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -5,6 +5,7 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.graphics.drawable.ColorDrawable +import android.net.Uri import android.os.Build import android.os.Bundle import android.support.annotation.StringRes @@ -17,7 +18,10 @@ import android.support.v4.view.ViewPager import android.support.v7.widget.Toolbar import android.view.Menu import android.view.MenuItem +import android.webkit.ValueCallback +import android.webkit.WebChromeClient import ca.allanwang.kau.changelog.showChangelog +import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult import ca.allanwang.kau.searchview.SearchItem import ca.allanwang.kau.searchview.SearchView import ca.allanwang.kau.searchview.bindSearchView @@ -35,6 +39,9 @@ import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.materialdrawer.AccountHeader import com.mikepenz.materialdrawer.Drawer +import com.pitchedapps.frost.contracts.ActivityWebContract +import com.pitchedapps.frost.contracts.FileChooserContract +import com.pitchedapps.frost.contracts.FileChooserDelegate import com.pitchedapps.frost.dbflow.loadFbCookie import com.pitchedapps.frost.dbflow.loadFbTabs import com.pitchedapps.frost.facebook.FbCookie @@ -53,7 +60,8 @@ import io.reactivex.subjects.PublishSubject import org.jsoup.Jsoup import java.util.concurrent.TimeUnit -class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { +class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, + ActivityWebContract, FileChooserContract by FileChooserDelegate() { lateinit var adapter: SectionsPagerAdapter val toolbar: Toolbar by bindView(R.id.toolbar) @@ -366,7 +374,12 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { return true } + override fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { + openFileChooser(this, filePathCallback, fileChooserParams) + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (onActivityResultWeb(requestCode, resultCode, data)) return super.onActivityResult(requestCode, resultCode, data) if (requestCode == ACTIVITY_SETTINGS) { when (resultCode) { @@ -391,6 +404,11 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { } } + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + kauOnRequestPermissionsResult(permissions, grantResults) + } + override fun onResume() { super.onResume() FbCookie.switchBackUser { } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt index dd7f60b7..1bd4777d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt @@ -1,14 +1,21 @@ package com.pitchedapps.frost import android.content.Intent +import android.net.Uri import android.os.Bundle import android.support.design.widget.CoordinatorLayout import android.support.design.widget.Snackbar import android.support.v7.app.AppCompatActivity import android.support.v7.widget.Toolbar +import android.webkit.ValueCallback +import android.webkit.WebChromeClient +import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult import ca.allanwang.kau.utils.* import com.jude.swipbackhelper.SwipeBackHelper import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.pitchedapps.frost.contracts.ActivityWebContract +import com.pitchedapps.frost.contracts.FileChooserContract +import com.pitchedapps.frost.contracts.FileChooserDelegate import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.web.FrostWebView @@ -17,7 +24,8 @@ import com.pitchedapps.frost.web.FrostWebView /** * Created by Allan Wang on 2017-06-01. */ -open class WebOverlayActivity : AppCompatActivity() { +open class WebOverlayActivity : AppCompatActivity(), + ActivityWebContract, FileChooserContract by FileChooserDelegate() { val toolbar: Toolbar by bindView(R.id.overlay_toolbar) val frostWeb: FrostWebView by bindView(R.id.overlay_frost_webview) @@ -106,4 +114,17 @@ open class WebOverlayActivity : AppCompatActivity() { finishSlideOut() } } + + override fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { + openFileChooser(this, filePathCallback, fileChooserParams) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (onActivityResultWeb(requestCode, resultCode, data)) return + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + kauOnRequestPermissionsResult(permissions, grantResults) + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt new file mode 100644 index 00000000..5b2cfa49 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/FileChooser.kt @@ -0,0 +1,56 @@ +package com.pitchedapps.frost.contracts + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import android.webkit.ValueCallback +import android.webkit.WebChromeClient +import ca.allanwang.kau.permissions.PERMISSION_READ_EXTERNAL_STORAGE +import ca.allanwang.kau.permissions.kauRequestPermissions +import com.pitchedapps.frost.utils.L + +/** + * Created by Allan Wang on 2017-07-04. + */ +const val FILE_CHOOSER_REQUEST = 67 + +interface FileChooserActivityContract { + fun openFileChooser(filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) +} + +interface FileChooserContract { + var filePathCallback: ValueCallback>? + fun openFileChooser(activity: Activity, filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) + fun onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean +} + +class FileChooserDelegate : FileChooserContract { + + override var filePathCallback: ValueCallback>? = null + + override fun openFileChooser(activity: Activity, filePathCallback: ValueCallback>, fileChooserParams: WebChromeClient.FileChooserParams) { + activity.kauRequestPermissions(PERMISSION_READ_EXTERNAL_STORAGE) { + granted, _ -> + if (!granted) return@kauRequestPermissions + val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT) + contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE) + contentSelectionIntent.type = fileChooserParams.acceptTypes?.joinToString(separator = "|") ?: "*/*" + activity.startActivityForResult(contentSelectionIntent, FILE_CHOOSER_REQUEST) + this.filePathCallback?.onReceiveValue(null) + this.filePathCallback = filePathCallback + } + } + + override fun onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean { + L.d("On activity results web $requestCode") + if (requestCode != FILE_CHOOSER_REQUEST) return false + var results: Uri? = null + + if (resultCode == Activity.RESULT_OK && intent != null) results = Uri.parse(intent.dataString) + L.d("Callback received; ${filePathCallback != null}") + filePathCallback?.onReceiveValue(if (results == null) null else arrayOf(results)) + filePathCallback = null + return true + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/WebContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/WebContract.kt new file mode 100644 index 00000000..2485a468 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/WebContract.kt @@ -0,0 +1,8 @@ +package com.pitchedapps.frost.contracts + +/** + * Created by Allan Wang on 2017-07-04. + * + * Combination of all the core functions implemented by the Activity + */ +interface ActivityWebContract : FileChooserActivityContract \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt index 00e72cc7..363bf795 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt @@ -2,10 +2,13 @@ package com.pitchedapps.frost.web import android.net.Uri import android.webkit.* +import ca.allanwang.kau.utils.snackbar +import com.pitchedapps.frost.contracts.ActivityWebContract import com.pitchedapps.frost.utils.L import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.Subject + /** * Created by Allan Wang on 2017-05-31. */ @@ -13,6 +16,7 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() { val progressObservable: Subject = webCore.progressObservable val titleObservable: BehaviorSubject = webCore.titleObservable + val activityContract = (webCore.context as? ActivityWebContract) override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { L.i("Chrome Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}") @@ -30,15 +34,9 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() { progressObservable.onNext(newProgress) } - override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback>?, fileChooserParams: FileChooserParams?): Boolean { - L.d("On show file chooser") - fileChooserParams?.apply { - L.d(filenameHint ?: "hi") - L.d("$mode") - L.d(acceptTypes.contentToString()) - } - - return super.onShowFileChooser(webView, filePathCallback, fileChooserParams) + override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback>, fileChooserParams: FileChooserParams): Boolean { + activityContract?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.snackbar("File chooser not found") + return activityContract != null } override fun onGeolocationPermissionsShowPrompt(origin: String, callback: GeolocationPermissions.Callback) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt index 709ab7ac..62c28527 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt @@ -11,10 +11,7 @@ import com.pitchedapps.frost.SelectorActivity import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.injectors.* -import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.Prefs -import com.pitchedapps.frost.utils.cookies -import com.pitchedapps.frost.utils.launchNewTask +import com.pitchedapps.frost.utils.* import io.reactivex.subjects.Subject /** @@ -76,8 +73,20 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { L.d("Emit $flag") } - override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest?): Boolean { - L.i("Url Loading ${request?.url?.path}") + /** + * Helper to format the request and launch it + * returns true to override the url + */ + private fun launchRequest(request: WebResourceRequest): Boolean { + L.d("Launching ${request.url}") + webCore.context.launchWebOverlay(request.url.toString()) + return true + } + + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { + L.i("Url Loading ${request.url}") + val path = request.url.path + if (path.startsWith("/composer/")) return launchRequest(request) return super.shouldOverrideUrlLoading(view, request) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt index 83dccb9a..fb2e1851 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt @@ -16,6 +16,7 @@ import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.utils.L import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject +import org.jetbrains.anko.runOnUiThread import org.jsoup.Jsoup import java.util.concurrent.TimeUnit @@ -61,8 +62,11 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi Jsoup.parse(it).select("a:not([rel*='keywords(']):not([href=#])[rel]").map { element -> //split text into separate items + L.i("Search element ${element.attr("href")}") val texts = element.select("div").map { (it.ownText()) }.filter { it.isNotBlank() } - Pair(texts, element.attr("href")) + val pair = Pair(texts, element.attr("href")) + L.i("Search element potential $pair") + pair }.filter { it.first.isNotEmpty() } } .filter { content -> Pair(content.lastOrNull()?.second, content.size) != previousResult } @@ -72,7 +76,6 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi L.d("Search element count ${content.size}") contract.emitSearchResponse(content.map { (texts, href) -> - L.i("Search element $texts $href") SearchItem(href, texts[0], texts.getOrNull(1)) }) } @@ -86,7 +89,7 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi var pauseLoad: Boolean get() = settings.blockNetworkLoads set(value) { - settings.blockNetworkLoads = value + context.runOnUiThread { settings.blockNetworkLoads = value } } override fun reload() { @@ -97,8 +100,9 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi * Sets the input to have our given text, then dispatches the input event so the webpage recognizes it */ fun query(input: String) { + pauseLoad = false L.d("Searching attempt", input) - JsBuilder().js("var e=document.getElementById('main-search-input');if(e){e.value='$input';var n=new Event('input',{bubbles:!0,cancelable:!0});e.dispatchEvent(n)}else console.log('Input field not found')").build().inject(this) + JsBuilder().js("var e=document.getElementById('main-search-input');if(e){e.value='$input';var n=new Event('input',{bubbles:!0,cancelable:!0});e.dispatchEvent(n),e.dispatchEvent(new Event('focus'))}else console.log('Input field not found');").build().inject(this) } /** @@ -118,7 +122,8 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi inner class SearchJSI { @JavascriptInterface fun handleHtml(html: String) { - L.d("Search received response") + L.d("Search received response ${contract.isSearchOpened}") + if (!contract.isSearchOpened) pauseLoad = true searchSubject.onNext(html) } @@ -127,7 +132,6 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi when (flag) { 0 -> { L.d("Search loaded successfully") - if (!contract.isSearchOpened) pauseLoad = true } 1 -> { //something is not found in the search view; this is effectively useless L.eThrow("Search subject error; reverting to full overlay") diff --git a/gradle.properties b/gradle.properties index b86e4480..edf16070 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ MIN_SDK=21 TARGET_SDK=26 BUILD_TOOLS=26.0.0 -KAU=d024e8db46 +KAU=fe4632c34a KOTLIN=1.1.3 MATERIAL_DRAWER=5.9.3 MATERIAL_DRAWER_KT=1.0.4 -- cgit v1.2.3