From fe51373f5a95323d64f6d966888a2c6c62a36deb Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sun, 11 Mar 2018 19:24:32 -0400 Subject: Enhancement/debug mode (#779) * Update changelog * Improve debugger * Remove need for mapping urls * Remove excess logs * Clean up --- .../pitchedapps/frost/activities/AboutActivity.kt | 16 +++-- .../pitchedapps/frost/activities/DebugActivity.kt | 42 ++++++++++-- .../pitchedapps/frost/activities/ImageActivity.kt | 3 +- .../pitchedapps/frost/activities/LoginActivity.kt | 74 +++++++++++----------- .../frost/activities/SettingsActivity.kt | 2 +- .../frost/contracts/ActivityContract.kt | 1 + .../pitchedapps/frost/contracts/VideoViewHolder.kt | 3 +- .../com/pitchedapps/frost/dbflow/NotificationDb.kt | 3 +- .../pitchedapps/frost/debugger/OfflineWebsite.kt | 27 ++++++-- .../frost/facebook/requests/FbRequest.kt | 2 + .../pitchedapps/frost/facebook/requests/Images.kt | 6 +- .../frost/fragments/FragmentContract.kt | 1 - .../com/pitchedapps/frost/injectors/CssHider.kt | 2 +- .../com/pitchedapps/frost/injectors/JsActions.kt | 3 +- .../com/pitchedapps/frost/injectors/JsInjector.kt | 3 +- .../pitchedapps/frost/intro/IntroFragmentTheme.kt | 3 +- .../pitchedapps/frost/intro/IntroImageFragments.kt | 3 +- .../frost/services/FrostNotifications.kt | 3 +- .../kotlin/com/pitchedapps/frost/settings/Debug.kt | 15 ++--- .../frost/utils/AnimatedVectorDelegate.kt | 6 +- .../kotlin/com/pitchedapps/frost/utils/Utils.kt | 8 ++- .../com/pitchedapps/frost/utils/iab/IabBinder.kt | 4 +- .../com/pitchedapps/frost/views/AccountItem.kt | 18 +++--- .../pitchedapps/frost/views/FrostVideoViewer.kt | 1 - .../kotlin/com/pitchedapps/frost/views/Keywords.kt | 3 +- .../com/pitchedapps/frost/web/DebugWebView.kt | 30 ++++----- .../pitchedapps/frost/web/FrostChromeClients.kt | 3 +- .../frost/web/FrostRequestInterceptor.kt | 13 ++-- .../com/pitchedapps/frost/web/NestedWebView.kt | 12 ++-- app/src/main/res/values/strings_pref_debug.xml | 1 + app/src/main/res/xml/frost_changelog.xml | 10 +-- .../frost/debugger/OfflineWebsiteTest.kt | 2 +- .../frost/utils/StringEscapeUtilsTest.kt | 16 +++++ 33 files changed, 198 insertions(+), 141 deletions(-) create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/utils/StringEscapeUtilsTest.kt (limited to 'app/src') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt index a4d98d8a..691f050e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt @@ -59,7 +59,7 @@ class AboutActivity : AboutActivityBase(null, { "subsamplingscaleimageview" ) - val l = libs.prepareLibraries(this, include, null, false, true,true) + val l = libs.prepareLibraries(this, include, null, false, true, true) // l.forEach { KL.d{"Lib ${it.definedName}"} } return l } @@ -88,14 +88,18 @@ class AboutActivity : AboutActivityBase(null, { if (item is LibraryIItem) { val now = System.currentTimeMillis() if (now - lastClick > 500) - clickCount = 0 + clickCount = 1 else clickCount++ lastClick = now - if (clickCount == 7 && !Prefs.debugSettings) { - Prefs.debugSettings = true - L.d { "Debugging section enabled" } - toast(R.string.debug_toast_enabled) + if (clickCount == 8) { + if (!Prefs.debugSettings) { + Prefs.debugSettings = true + L.d { "Debugging section enabled" } + toast(R.string.debug_toast_enabled) + } else { + toast(R.string.debug_toast_already_enabled) + } } } false diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt index 685e6532..30f12c1d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/DebugActivity.kt @@ -15,10 +15,15 @@ import ca.allanwang.kau.utils.visible import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.pitchedapps.frost.R import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.injectors.JsActions +import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.createFreshDir import com.pitchedapps.frost.utils.setFrostColors import com.pitchedapps.frost.web.DebugWebView +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers import java.io.File /** @@ -33,6 +38,8 @@ class DebugActivity : KauBaseActivity() { companion object { const val RESULT_URL = "extra_result_url" + const val RESULT_SCREENSHOT = "extra_result_screenshot" + const val RESULT_BODY = "extra_result_body" fun baseDir(context: Context) = File(context.externalCacheDir, "offline_debug") } @@ -61,13 +68,34 @@ class DebugActivity : KauBaseActivity() { val parent = baseDir(this) parent.createFreshDir() - val file = File(parent, "screenshot.png") - web.getScreenshot(file) { - val intent = Intent() - intent.putExtra(RESULT_URL, web.url) - setResult(Activity.RESULT_OK, intent) - finish() - } + val rxScreenshot = Single.fromCallable { + web.getScreenshot(File(parent, "screenshot.png")) + }.subscribeOn(Schedulers.io()) + val rxBody = Single.create { emitter -> + web.evaluateJavascript(JsActions.RETURN_BODY.function) { + emitter.onSuccess(it) + } + }.subscribeOn(AndroidSchedulers.mainThread()) + Single.zip(listOf(rxScreenshot, rxBody), { + val screenshot = it[0] == true + val body = it[1] as? String + screenshot to body + }).observeOn(AndroidSchedulers.mainThread()) + .subscribe { (screenshot, body), err -> + if (err != null) { + L.e { "DebugActivity error ${err.message}" } + setResult(Activity.RESULT_CANCELED) + finish() + return@subscribe + } + val intent = Intent() + intent.putExtra(RESULT_URL, web.url) + intent.putExtra(RESULT_SCREENSHOT, screenshot) + if (body != null) + intent.putExtra(RESULT_BODY, body) + setResult(Activity.RESULT_OK, intent) + finish() + } } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt index e563ff8a..9f191460 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -92,7 +92,8 @@ class ImageActivity : KauBaseActivity() { // a unique image identifier based on the id (if it exists), and its hash private val IMAGE_HASH: String by lazy { - "${Math.abs(FB_IMAGE_ID_MATCHER.find(IMAGE_URL)[1]?.hashCode() ?: 0)}_${Math.abs(IMAGE_URL.hashCode())}" + "${Math.abs(FB_IMAGE_ID_MATCHER.find(IMAGE_URL)[1]?.hashCode() + ?: 0)}_${Math.abs(IMAGE_URL.hashCode())}" } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt index 3b320cce..aa2e5871 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt @@ -83,32 +83,32 @@ class LoginActivity : BaseActivity() { usernameSubject, BiFunction(::Pair)) .observeOn(AndroidSchedulers.mainThread()).subscribe { (foundImage, name) -> - refresh = false - if (!foundImage) { - L.e { "Could not get profile photo; Invalid userId?" } - L._i { cookie } - } - textview.text = String.format(getString(R.string.welcome), name) - textview.fadeIn() - frostAnswers { - logLogin(LoginEvent() - .putMethod("frost_browser") - .putSuccess(true)) - } - /* - * The user may have logged into an account that is already in the database - * We will let the db handle duplicates and load it now after the new account has been saved - */ - loadFbCookiesAsync { - val cookies = ArrayList(it) - Handler().postDelayed({ - if (Showcase.intro) - launchNewTask(cookies, true) - else - launchNewTask(cookies, true) - }, 1000) - } - } + refresh = false + if (!foundImage) { + L.e { "Could not get profile photo; Invalid userId?" } + L._i { cookie } + } + textview.text = String.format(getString(R.string.welcome), name) + textview.fadeIn() + frostAnswers { + logLogin(LoginEvent() + .putMethod("frost_browser") + .putSuccess(true)) + } + /* + * The user may have logged into an account that is already in the database + * We will let the db handle duplicates and load it now after the new account has been saved + */ + loadFbCookiesAsync { + val cookies = ArrayList(it) + Handler().postDelayed({ + if (Showcase.intro) + launchNewTask(cookies, true) + else + launchNewTask(cookies, true) + }, 1000) + } + } loadProfile(cookie.id) loadUsername(cookie) } @@ -117,17 +117,17 @@ class LoginActivity : BaseActivity() { private fun loadProfile(id: Long) { profileLoader.load(PROFILE_PICTURE_URL(id)) .transform(FrostGlide.roundCorner).listener(object : RequestListener { - override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { - profileSubject.onSuccess(true) - return false - } - - override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { - e.logFrostAnswers("Profile loading exception") - profileSubject.onSuccess(false) - return false - } - }).into(profile) + override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { + profileSubject.onSuccess(true) + return false + } + + override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { + e.logFrostAnswers("Profile loading exception") + profileSubject.onSuccess(false) + return false + } + }).into(profile) } private fun loadUsername(cookie: CookieModel) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt index 2de7a843..8d4e521f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt @@ -52,7 +52,7 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IabSettings() { ACTIVITY_REQUEST_DEBUG -> { val url = data?.extras?.getString(DebugActivity.RESULT_URL) if (resultCode == Activity.RESULT_OK && url?.isNotBlank() == true) - sendDebug(url) + sendDebug(url, data.extras.getString(DebugActivity.RESULT_BODY)) return } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt index 43463752..ca6df366 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/ActivityContract.kt @@ -17,6 +17,7 @@ interface MainActivityContract : ActivityContract, MainFabContract { * Available on all threads */ fun collapseAppBar() + fun reloadFragment(fragment: BaseFragment) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewHolder.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewHolder.kt index fc75ba14..13b6a7aa 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewHolder.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewHolder.kt @@ -15,8 +15,7 @@ interface VideoViewHolder : FrameWrapper, FrostVideoContainerContract { var videoViewer: FrostVideoViewer? - fun showVideo(url: String) - = showVideo(url, false) + fun showVideo(url: String) = showVideo(url, false) /** * Create new viewer and reuse existing one diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt index 88a1383d..60f8c5a2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt @@ -29,7 +29,8 @@ class NotificationMigration2(modelClass: Class) : AlterTableM @Table(database = NotificationDb::class, allFields = true, primaryKeyConflict = ConflictAction.REPLACE) data class NotificationModel(@PrimaryKey var id: Long = -1L, var epoch: Long = -1L, var epochIm: Long = -1) : BaseModel() -fun lastNotificationTime(id: Long): NotificationModel = (select from NotificationModel::class where (NotificationModel_Table.id eq id)).querySingle() ?: NotificationModel(id = id) +fun lastNotificationTime(id: Long): NotificationModel = (select from NotificationModel::class where (NotificationModel_Table.id eq id)).querySingle() + ?: NotificationModel(id = id) fun saveNotificationTime(notificationModel: NotificationModel, callback: (() -> Unit)? = null) { notificationModel.async save { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/debugger/OfflineWebsite.kt b/app/src/main/kotlin/com/pitchedapps/frost/debugger/OfflineWebsite.kt index 791e6a69..95162932 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/debugger/OfflineWebsite.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/debugger/OfflineWebsite.kt @@ -9,8 +9,11 @@ import com.pitchedapps.frost.facebook.requests.zip import com.pitchedapps.frost.utils.createFreshDir import com.pitchedapps.frost.utils.createFreshFile import com.pitchedapps.frost.utils.frostJsoup +import com.pitchedapps.frost.utils.unescapeHtml import okhttp3.Request import okhttp3.ResponseBody +import org.jsoup.Jsoup +import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.nodes.Entities import java.io.File @@ -29,6 +32,8 @@ import java.util.zip.ZipOutputStream */ class OfflineWebsite(private val url: String, private val cookie: String = "", + baseUrl: String? = null, + private val html: String? = null, /** * Directory that holds all the files */ @@ -38,7 +43,8 @@ class OfflineWebsite(private val url: String, /** * Supplied url without the queries */ - val baseUrl = url.substringBefore("?").trim('/') + private val baseUrl = (baseUrl ?: url.substringBefore("?") + .substringBefore(".com")).trim('/') private val mainFile = File(baseDir, "index.html") private val assetDir = File(baseDir, "assets") @@ -50,7 +56,7 @@ class OfflineWebsite(private val url: String, private val L = KauLoggerExtension("Offline", com.pitchedapps.frost.utils.L) init { - if (!baseUrl.startsWith("http")) + if (!this.baseUrl.startsWith("http")) throw IllegalArgumentException("Base Url must start with http") } @@ -94,7 +100,13 @@ class OfflineWebsite(private val url: String, if (cancelled) return - val doc = frostJsoup(cookie, url) + val doc: Document + if (html == null || html.length < 100) { + doc = frostJsoup(cookie, url) + } else { + doc = Jsoup.parse("${html.unescapeHtml()}") + L.d { "Building data from supplied content of size ${html.length}" } + } doc.setBaseUri(baseUrl) doc.outputSettings().escapeMode(Entities.EscapeMode.extended) if (doc.childNodeSize() == 0) { @@ -125,8 +137,11 @@ class OfflineWebsite(private val url: String, progress(50) downloadCss().subscribe { cssLinks, cssThrowable -> + if (cssThrowable != null) { - L.e { "CSS parsing failed" } + L.e { "CSS parsing failed: ${cssThrowable.message} $cssThrowable" } + callback(false) + return@subscribe } progress(70) @@ -291,8 +306,8 @@ class OfflineWebsite(private val url: String, private fun String.shorten() = if (length <= 10) this else substring(length - 10) - private fun Set.clean() - = filter(String::isNotBlank).filter { it.startsWith("http") } + private fun Set.clean(): List = + filter(String::isNotBlank).filter { it.startsWith("http") } private fun reset() { cancelled = false diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt index 45545336..c70ae1ae 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt @@ -130,6 +130,8 @@ fun String.getAuth(): RequestAuth { inline fun Array.zip(crossinline mapper: (List) -> O, crossinline caller: (T) -> R): Single { + if (isEmpty()) + return Single.just(mapper(emptyList())) val singles = map { Single.fromCallable { caller(it) }.subscribeOn(Schedulers.io()) } return Single.zip(singles) { val results = it.mapNotNull { it as? R } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt index 094e7fc9..453400d3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt @@ -76,7 +76,8 @@ class HdImageLoading : ModelLoader { class HdImageFetcher(private val model: HdImageMaybe) : DataFetcher { - @Volatile private var cancelled: Boolean = false + @Volatile + private var cancelled: Boolean = false private var urlCall: Call? = null private var inputStream: InputStream? = null @@ -92,7 +93,8 @@ class HdImageFetcher(private val model: HdImageMaybe) : DataFetcher if (!model.isValid) return callback.fail("Model is invalid") model.cookie.fbRequest(fail = { callback.fail("Invalid auth") }) { if (cancelled) return@fbRequest callback.fail("Cancelled") - val url = getFullSizedImage(model.id).invoke() ?: return@fbRequest callback.fail("Null url") + val url = getFullSizedImage(model.id).invoke() + ?: return@fbRequest callback.fail("Null url") if (cancelled) return@fbRequest callback.fail("Cancelled") if (!url.contains("png") && !url.contains("jpg")) return@fbRequest callback.fail("Invalid format") urlCall = Request.Builder().url(url).get().call() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt index e683b056..6555e076 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt @@ -1,6 +1,5 @@ package com.pitchedapps.frost.fragments -import android.support.design.widget.FloatingActionButton import com.pitchedapps.frost.contracts.* import com.pitchedapps.frost.views.FrostRecyclerView import io.reactivex.disposables.Disposable diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt index 637a5092..6981fd1c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt @@ -9,7 +9,7 @@ import android.webkit.WebView */ enum class CssHider(vararg val items: String) : InjectorContract { CORE("[data-sigil=m_login_upsell]", "role=progressbar"), -// HEADER("#header", "[data-sigil=MTopBlueBarHeader]", + // HEADER("#header", "[data-sigil=MTopBlueBarHeader]", // "#header-notices", "[data-sigil*=m-promo-jewel-header]"), ADS("article[data-xt*=sponsor]", "article[data-store*=sponsor]"), diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt index 7be8cd3c..b4926355 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsActions.kt @@ -16,6 +16,7 @@ enum class JsActions(body: String) : InjectorContract { LOGIN_CHECK("document.getElementById('signup-button')&&Frost.loadLogin();"), BASE_HREF("""document.write("");"""), FETCH_BODY("""setTimeout(function(){var e=document.querySelector("main");e||(e=document.querySelector("body")),Frost.handleHtml(e.outerHTML)},1e2);"""), + RETURN_BODY("return(document.getElementsByTagName('html')[0].innerHTML);"), CREATE_POST(clickBySelector("button[name=view_overview]")), // CREATE_MSG(clickBySelector("a[rel=dialog]")), /** @@ -23,7 +24,7 @@ enum class JsActions(body: String) : InjectorContract { */ EMPTY(""); - val function = "!function(){$body}();" + val function = "(function(){$body})();" override fun inject(webView: WebView, callback: (() -> Unit)?) = JsInjector(function).inject(webView, callback) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt index 2d067e44..1698ae13 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -101,8 +101,7 @@ fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Int) -> Uni } fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract, - callback: ((Int) -> Unit)? = null) - = web.jsInject(*injectors, callback = callback) + callback: ((Int) -> Unit)? = null) = web.jsInject(*injectors, callback = callback) /** * Wrapper class to convert a function into an injector diff --git a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt index f91d77ea..42630d79 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroFragmentTheme.kt @@ -22,8 +22,7 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) { val themeList get() = listOf(light, dark, amoled, glass) - override fun viewArray(): Array> - = arrayOf(arrayOf(title), arrayOf(light, dark), arrayOf(amoled, glass)) + override fun viewArray(): Array> = arrayOf(arrayOf(title), arrayOf(light, dark), arrayOf(amoled, glass)) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt index b9c83c50..c0ccd649 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/intro/IntroImageFragments.kt @@ -25,8 +25,7 @@ abstract class BaseImageIntroFragment( val screen: Drawable by lazyResettableRegistered { imageDrawable.findDrawableByLayerId(R.id.intro_phone_screen) } val icon: ImageView by bindViewResettable(R.id.intro_button) - override fun viewArray(): Array> - = arrayOf(arrayOf(title), arrayOf(desc)) + override fun viewArray(): Array> = arrayOf(arrayOf(title), arrayOf(desc)) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { title.setText(titleRes) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt index b6427e5b..30c94744 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -1,6 +1,5 @@ package com.pitchedapps.frost.services -import android.annotation.SuppressLint import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager @@ -93,7 +92,7 @@ fun NotificationCompat.Builder.setFrostAlert(enable: Boolean, ringtone: String): if (enable) Notification.GROUP_ALERT_CHILDREN else Notification.GROUP_ALERT_SUMMARY) } else if (!enable) { - setDefaults(0) + setDefaults(0) } else { var defaults = 0 if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt index df0fdef2..5dc0edab 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Debug.kt @@ -10,6 +10,7 @@ import com.pitchedapps.frost.activities.DebugActivity import com.pitchedapps.frost.activities.SettingsActivity import com.pitchedapps.frost.activities.SettingsActivity.Companion.ACTIVITY_REQUEST_DEBUG import com.pitchedapps.frost.debugger.OfflineWebsite +import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.parsers.FrostParser @@ -90,18 +91,12 @@ private fun Context.createEmail(parser: FrostParser<*>, content: Any?) = private const val ZIP_NAME = "debug" -fun SettingsActivity.sendDebug(urlOrig: String) { - - val url = when { - urlOrig.endsWith("soft=requests") -> FbItem.FRIENDS.url - urlOrig.endsWith("soft=messages") -> FbItem.MESSAGES.url - urlOrig.endsWith("soft=notifications") -> FbItem.NOTIFICATIONS.url - urlOrig.endsWith("soft=search") -> "${FbItem._SEARCH.url}?q=a" - else -> urlOrig - } +fun SettingsActivity.sendDebug(url: String, html: String?) { val downloader = OfflineWebsite(url, FbCookie.webCookie ?: "", - DebugActivity.baseDir(this)) + baseUrl = FB_URL_BASE, + html = html, + baseDir = DebugActivity.baseDir(this)) val md = materialDialog { title(R.string.parsing_data) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/AnimatedVectorDelegate.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/AnimatedVectorDelegate.kt index 928f90d3..8d800e9b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/AnimatedVectorDelegate.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/AnimatedVectorDelegate.kt @@ -54,8 +54,10 @@ class AnimatedVectorDelegate( override fun bind(view: ImageView) { this.view = view - view.context.drawable(avdStart) as? AnimatedVectorDrawable ?: throw IllegalArgumentException("AnimatedVectorDelegate has a starting drawable that isn't an avd") - view.context.drawable(avdEnd) as? AnimatedVectorDrawable ?: throw IllegalArgumentException("AnimatedVectorDelegate has an ending drawable that isn't an avd") + view.context.drawable(avdStart) as? AnimatedVectorDrawable + ?: throw IllegalArgumentException("AnimatedVectorDelegate has a starting drawable that isn't an avd") + view.context.drawable(avdEnd) as? AnimatedVectorDrawable + ?: throw IllegalArgumentException("AnimatedVectorDelegate has an ending drawable that isn't an avd") view.setImageResource(avdStart) if (emitOnBind) animatedVectorListener?.invoke(avd!!, false) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt index 3e102671..d73f29e9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -32,6 +32,7 @@ import com.pitchedapps.frost.dbflow.CookieModel import com.pitchedapps.frost.facebook.* import com.pitchedapps.frost.facebook.FbUrlFormatter.Companion.VIDEO_REDIRECT import com.pitchedapps.frost.utils.iab.IS_FROST_PRO +import org.apache.commons.text.StringEscapeUtils import org.jsoup.Jsoup import org.jsoup.nodes.Element import java.io.File @@ -342,4 +343,9 @@ fun File.createFreshDir(): Boolean { if (exists() && !deleteRecursively()) return false return mkdirs() -} \ No newline at end of file +} + +fun String.unescapeHtml(): String = + StringEscapeUtils.unescapeXml(this) + .replace("\\u003C", "<") + .replace("\\\"", "\"") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt index cdfffb87..568127a2 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IabBinder.kt @@ -94,8 +94,8 @@ abstract class IabBinder : FrostBilling { error.logFrostAnswers("IAB error $errorCode") } - override fun onActivityResultBilling(requestCode: Int, resultCode: Int, data: Intent?): Boolean - = bp?.handleActivityResult(requestCode, resultCode, data) ?: false + override fun onActivityResultBilling(requestCode: Int, resultCode: Int, data: Intent?): Boolean = bp?.handleActivityResult(requestCode, resultCode, data) + ?: false override fun purchasePro() { val bp = this.bp diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt index 78847df4..7058ffe4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt @@ -34,16 +34,16 @@ class AccountItem(val cookie: CookieModel?) : KauIItem { - override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { - text.fadeIn() - return false - } + override fun onResourceReady(resource: Drawable?, model: Any?, target: Target?, dataSource: DataSource?, isFirstResource: Boolean): Boolean { + text.fadeIn() + return false + } - override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { - text.fadeIn() - return false - } - }).into(image) + override fun onLoadFailed(e: GlideException?, model: Any?, target: Target?, isFirstResource: Boolean): Boolean { + text.fadeIn() + return false + } + }).into(image) } else { text.visible() image.setImageDrawable(GoogleMaterial.Icon.gmd_add_circle_outline.toDrawable(itemView.context, 100, Prefs.textColor)) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt index aef9099a..ceb3d487 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt @@ -16,7 +16,6 @@ import ca.allanwang.kau.utils.* import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.pitchedapps.frost.R -import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostDownload diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt index 9edd671b..d005e58a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/Keywords.kt @@ -52,8 +52,7 @@ class Keywords @JvmOverloads constructor( recycler.layoutManager = LinearLayoutManager(context) recycler.adapter = adapter adapter.withEventHook(object : ClickEventHook() { - override fun onBind(viewHolder: RecyclerView.ViewHolder): View? - = (viewHolder as? KeywordItem.ViewHolder)?.delete + override fun onBind(viewHolder: RecyclerView.ViewHolder): View? = (viewHolder as? KeywordItem.ViewHolder)?.delete override fun onClick(v: View, position: Int, fastAdapter: FastAdapter, item: KeywordItem) { adapter.remove(position) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt index 7fd2286c..4ac9e600 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/DebugWebView.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import android.graphics.Color +import android.support.annotation.WorkerThread import android.util.AttributeSet import android.view.View import android.webkit.WebView @@ -16,8 +17,6 @@ import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.createFreshFile import com.pitchedapps.frost.utils.iab.IS_FROST_PRO import com.pitchedapps.frost.utils.isFacebookUrl -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread import org.jetbrains.anko.withAlpha import java.io.File @@ -45,27 +44,22 @@ class DebugWebView @JvmOverloads constructor( isDrawingCacheEnabled = true } - fun getScreenshot(output: File, callback: (Boolean) -> Unit) { + @WorkerThread + fun getScreenshot(output: File): Boolean { if (!output.createFreshFile()) { L.e { "Failed to create ${output.absolutePath} for debug screenshot" } - return callback(false) + return false } - doAsync { - var valid = true - try { - output.outputStream().use { - drawingCache.compress(Bitmap.CompressFormat.PNG, 100, it) - } - L.d { "Created screenshot at ${output.absolutePath}" } - } catch (e: Exception) { - L.e { "An error occurred ${e.message}" } - valid = false - } finally { - uiThread { - callback(valid) - } + return try { + output.outputStream().use { + drawingCache.compress(Bitmap.CompressFormat.PNG, 100, it) } + L.d { "Created screenshot at ${output.absolutePath}" } + true + } catch (e: Exception) { + L.e { "An error occurred ${e.message}" } + false } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt index 4afdd8d2..3c3c063a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt @@ -46,7 +46,8 @@ class FrostChromeClient(web: FrostWebView) : WebChromeClient() { } override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback?>, fileChooserParams: FileChooserParams): Boolean { - activity?.openFileChooser(filePathCallback, fileChooserParams) ?: webView.frostSnackbar(R.string.file_chooser_not_found) + activity?.openFileChooser(filePathCallback, fileChooserParams) + ?: webView.frostSnackbar(R.string.file_chooser_not_found) return activity != null } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt index 0501e2e6..72e448b9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt @@ -46,15 +46,12 @@ val WebResourceRequest.isMedia: Boolean * Generic filter passthrough * If Resource is already nonnull, pass it, otherwise check if filter is met and override the response accordingly */ -fun WebResourceResponse?.filter(request: WebResourceRequest, filter: (url: String) -> Boolean) - = filter(request.query { filter(it) }) +fun WebResourceResponse?.filter(request: WebResourceRequest, filter: (url: String) -> Boolean) = filter(request.query { filter(it) }) -fun WebResourceResponse?.filter(filter: Boolean): WebResourceResponse? - = this ?: if (filter) blankResource else null +fun WebResourceResponse?.filter(filter: Boolean): WebResourceResponse? = this + ?: if (filter) blankResource else null -fun WebResourceResponse?.filterCss(request: WebResourceRequest): WebResourceResponse? - = filter(request) { it.endsWith(".css") } +fun WebResourceResponse?.filterCss(request: WebResourceRequest): WebResourceResponse? = filter(request) { it.endsWith(".css") } -fun WebResourceResponse?.filterImage(request: WebResourceRequest): WebResourceResponse? - = filter(request.isImage) +fun WebResourceResponse?.filterImage(request: WebResourceRequest): WebResourceResponse? = filter(request.isImage) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt index 8fd4c0b4..297c4158 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/NestedWebView.kt @@ -99,15 +99,11 @@ open class NestedWebView @JvmOverloads constructor( final override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent() - final override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?) - = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow) + final override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?) = childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow) - final override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?) - = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow) + final override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?) = childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow) - final override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean) - = childHelper.dispatchNestedFling(velocityX, velocityY, consumed) + final override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean) = childHelper.dispatchNestedFling(velocityX, velocityY, consumed) - final override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float) - = childHelper.dispatchNestedPreFling(velocityX, velocityY) + final override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float) = childHelper.dispatchNestedPreFling(velocityX, velocityY) } \ No newline at end of file diff --git a/app/src/main/res/values/strings_pref_debug.xml b/app/src/main/res/values/strings_pref_debug.xml index 771e5130..21e98311 100644 --- a/app/src/main/res/values/strings_pref_debug.xml +++ b/app/src/main/res/values/strings_pref_debug.xml @@ -2,6 +2,7 @@ Debugging section is enabled! Go back to settings. + Debugging section is already enabled. Go back to settings. Though most private content is automatically removed in the report, some sensitive info may still be visible. \nPlease have a look at the debug report before sending it. diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 7d479852..67461960 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -6,14 +6,16 @@ --> + + + + + + - - - - diff --git a/app/src/test/kotlin/com/pitchedapps/frost/debugger/OfflineWebsiteTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/debugger/OfflineWebsiteTest.kt index 9fbb6069..e30ec174 100644 --- a/app/src/test/kotlin/com/pitchedapps/frost/debugger/OfflineWebsiteTest.kt +++ b/app/src/test/kotlin/com/pitchedapps/frost/debugger/OfflineWebsiteTest.kt @@ -15,7 +15,7 @@ class OfflineWebsiteTest { fun basic() { val countdown = CountDownLatch(1) val buildPath = if (File(".").parentFile?.name == "app") "build/offline_test" else "app/build/offline_test" - OfflineWebsite(FB_URL_BASE, COOKIE, File(buildPath)) + OfflineWebsite(FB_URL_BASE, COOKIE, baseDir = File(buildPath)) .loadAndZip("test") { println("Outcome $it") countdown.countDown() diff --git a/app/src/test/kotlin/com/pitchedapps/frost/utils/StringEscapeUtilsTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/utils/StringEscapeUtilsTest.kt new file mode 100644 index 00000000..86bd4ce5 --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/utils/StringEscapeUtilsTest.kt @@ -0,0 +1,16 @@ +package com.pitchedapps.frost.utils + +import org.junit.Test +import kotlin.test.assertEquals + +/** + * Created by Allan Wang on 11/03/18. + */ +class StringEscapeUtilsTest { + + @Test + fun utf() { + val escaped = "\\u003Chead> color=\\\"#3b5998\\\"" + assertEquals(" color=\"#3b5998\"", escaped.unescapeHtml()) + } +} \ No newline at end of file -- cgit v1.2.3