aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-16 17:26:58 -0700
committerGitHub <noreply@github.com>2017-07-16 17:26:58 -0700
commitd90cb9b61cd2e033b46f4780ad1340c5f35b7751 (patch)
tree0294ce22bacb463c9cc95de8dc5581c1bd59a108 /app/src/main/kotlin/com/pitchedapps/frost
parentc3f1fc6a8b3216442a018bb04843dfa68d738918 (diff)
downloadfrost-d90cb9b61cd2e033b46f4780ad1340c5f35b7751.tar.gz
frost-d90cb9b61cd2e033b46f4780ad1340c5f35b7751.tar.bz2
frost-d90cb9b61cd2e033b46f4780ad1340c5f35b7751.zip
Add image viewing and downloading (#63)v1.3
* Commence aggressive image caching * Add glide toggle and css url parsing * Add image hook and refractor activities * Update version analytics * Implemented imageactivity but glide will not load * Create working image loader * Finalize image view * Finalize image view logic * Remove custom cache experiment
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt)4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/BaseActivity.kt)3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/FrostWebActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt)2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt297
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt)3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt)14
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/SelectorActivity.kt)3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt)4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt)3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt21
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt12
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt16
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt31
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt67
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt21
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt13
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt18
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt4
34 files changed, 550 insertions, 74 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
index 4b62d244..3f5bdeda 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt
@@ -10,6 +10,7 @@ import com.crashlytics.android.Crashlytics
import com.crashlytics.android.answers.Answers
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
+import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.CrashReportingTree
import com.pitchedapps.frost.utils.GlideApp
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
index 122c571e..12cb955d 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
@@ -2,6 +2,9 @@ package com.pitchedapps.frost
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
+import com.pitchedapps.frost.activities.LoginActivity
+import com.pitchedapps.frost.activities.MainActivity
+import com.pitchedapps.frost.activities.SelectorActivity
import com.pitchedapps.frost.dbflow.loadFbCookiesAsync
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.L
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
index 6cab2a59..63ad8bae 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.support.constraint.ConstraintLayout
import android.support.constraint.ConstraintSet
@@ -20,6 +20,8 @@ import com.mikepenz.fastadapter.IItem
import com.mikepenz.fastadapter.items.AbstractItem
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.typeface.IIcon
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.Prefs
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/BaseActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt
index c2551125..fd020af1 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/BaseActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt
@@ -1,7 +1,8 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.materialDialogThemed
import com.pitchedapps.frost.utils.setFrostTheme
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/FrostWebActivity.kt
index 3e337813..1773471f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/FrostWebActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.os.Bundle
import com.pitchedapps.frost.utils.Prefs
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
new file mode 100644
index 00000000..509ac2cb
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
@@ -0,0 +1,297 @@
+package com.pitchedapps.frost.activities
+
+import android.animation.ValueAnimator
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.content.res.ColorStateList
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.os.Bundle
+import android.os.Environment
+import android.support.design.widget.FloatingActionButton
+import android.support.v4.content.FileProvider
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ProgressBar
+import android.widget.TextView
+import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
+import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult
+import ca.allanwang.kau.permissions.kauRequestPermissions
+import ca.allanwang.kau.utils.*
+import com.bumptech.glide.request.target.BaseTarget
+import com.bumptech.glide.request.target.SizeReadyCallback
+import com.bumptech.glide.request.target.Target
+import com.bumptech.glide.request.transition.Transition
+import com.davemorrissey.labs.subscaleview.ImageSource
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+import com.mikepenz.google_material_typeface_library.GoogleMaterial
+import com.mikepenz.iconics.typeface.IIcon
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.utils.*
+import com.sothree.slidinguppanel.SlidingUpPanelLayout
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+import timber.log.Timber
+import java.io.File
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.*
+
+/**
+ * Created by Allan Wang on 2017-07-15.
+ */
+class ImageActivity : AppCompatActivity() {
+
+ val progress: ProgressBar by bindView(R.id.image_progress)
+ val container: ViewGroup by bindView(R.id.image_container)
+ val panel: SlidingUpPanelLayout? by bindOptionalView(R.id.image_panel)
+ val photo: SubsamplingScaleImageView by bindView(R.id.image_photo)
+ val caption: TextView? by bindOptionalView(R.id.image_text)
+ val fab: FloatingActionButton by bindView(R.id.image_fab)
+
+ /**
+ * Reference to the temporary file path
+ * Should be nonnull if the image is successfully loaded
+ * As this is temporary, the image is deleted upon exit
+ */
+ internal var tempFilePath: String? = null
+ /**
+ * Reference to path for downloaded image
+ * Nonnull once the image is downloaded by the user
+ */
+ internal var downloadPath: String? = null
+ /**
+ * Indicator for fab's click result
+ */
+ internal var fabAction: FabStates = FabStates.NOTHING
+ set(value) {
+ if (field == value) return
+ field = value
+ value.update(fab)
+ }
+
+ val imageUrl: String
+ get() = intent.extras.getString(ARG_IMAGE_URL)
+
+ val text: String?
+ get() = intent.extras.getString(ARG_TEXT)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(if (!text.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless)
+ container.setBackgroundColor(Prefs.bgColor.withMinAlpha(222))
+ caption?.setTextColor(Prefs.textColor)
+ caption?.setBackgroundColor(Prefs.bgColor.colorToForeground(0.1f).withAlpha(255))
+ caption?.text = text
+ progress.tint(Prefs.accentColor)
+ panel?.addPanelSlideListener(object : SlidingUpPanelLayout.SimplePanelSlideListener() {
+
+ override fun onPanelSlide(panel: View, slideOffset: Float) {
+ if (slideOffset == 0f && !fab.isShown) fab.show()
+ else if (slideOffset != 0f && fab.isShown) fab.hide()
+ caption?.alpha = slideOffset / 2 + 0.5f
+ }
+
+ })
+ fab.setOnClickListener { fabAction.onClick(this) }
+ photo.setOnImageEventListener(object : SubsamplingScaleImageView.DefaultOnImageEventListener() {
+ override fun onImageLoadError(e: Exception) {
+ L.e(e, "Image load error")
+ imageCallback(null, false)
+ }
+ })
+ GlideApp.with(this).asBitmap().load(imageUrl).into(PhotoTarget(this::imageCallback))
+ }
+
+ /**
+ * Callback to add image to view
+ * [resource] is guaranteed to be nonnull when [success] is true
+ * and null when it is false
+ */
+ private fun imageCallback(resource: Bitmap?, success: Boolean) {
+ if (progress.isVisible) progress.fadeOut()
+ if (success) {
+ saveTempImage(resource!!, {
+ if (it == null) {
+ imageCallback(null, false)
+ } else {
+ photo.setImage(ImageSource.uri(it))
+ fabAction = FabStates.DOWNLOAD
+ photo.animate().alpha(1f).scaleX(1f).scaleY(1f).withEndAction { fab.show() }.start()
+ }
+ })
+ } else {
+ fabAction = FabStates.ERROR
+ fab.show()
+ }
+ }
+
+ /**
+ * Bitmap load handler
+ */
+ class PhotoTarget(val callback: (resource: Bitmap?, success: Boolean) -> Unit) : BaseTarget<Bitmap>() {
+
+ override fun removeCallback(cb: SizeReadyCallback?) {}
+
+ override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) = callback(resource, true)
+
+ override fun onLoadFailed(errorDrawable: Drawable?) = callback(null, false)
+
+ override fun getSize(cb: SizeReadyCallback) = cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
+
+ }
+
+ private fun saveTempImage(resource: Bitmap, callback: (uri: Uri?) -> Unit) {
+ var photoFile: File? = null
+ try {
+ photoFile = createImageFile()
+ } catch (ignored: IOException) {
+ } finally {
+ if (photoFile == null) {
+ callback(null)
+ } else {
+ tempFilePath = photoFile.absolutePath
+ Timber.d("Temp image path $tempFilePath")
+ // File created; proceed with request
+ val photoURI = FileProvider.getUriForFile(this,
+ BuildConfig.APPLICATION_ID + ".provider",
+ photoFile)
+ photoFile.outputStream().use { resource.compress(Bitmap.CompressFormat.PNG, 100, it) }
+ callback(photoURI)
+ }
+ }
+ }
+
+ @Suppress("SIMPLE_DATE_FORMAT")
+ @Throws(IOException::class)
+ private fun createImageFile(): File {
+ // Create an image file name
+ @SuppressLint("SimpleDateFormat")
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
+ val imageFileName = "Frost_" + timeStamp + "_"
+ val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
+ return File.createTempFile(
+ imageFileName, /* prefix */
+ ".png", /* suffix */
+ storageDir /* directory */
+ )
+ }
+
+ internal fun downloadImage() {
+ kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) {
+ granted, _ ->
+ if (granted) {
+ doAsync {
+ val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
+ val imageFileName = "Frost_" + timeStamp + "_"
+ val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ val frostDir = File(storageDir, "Frost")
+ if (!frostDir.exists()) frostDir.mkdirs()
+ val destination = File.createTempFile(imageFileName, ".png", frostDir)
+ downloadPath = destination.absolutePath
+ var success = true
+ try {
+ File(tempFilePath).copyTo(destination, true)
+ } catch (e: Exception) {
+ success = false
+ } finally {
+ uiThread {
+ snackbar(if (success) R.string.image_download_success else R.string.image_download_fail)
+ if (success) {
+ deleteTempFile()
+ fabAction = FabStates.SHARE
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ internal fun deleteTempFile() {
+ if (tempFilePath != null) {
+ File(tempFilePath!!).delete()
+ tempFilePath = null
+ }
+ }
+
+ override fun onDestroy() {
+ deleteTempFile()
+ super.onDestroy()
+ }
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ kauOnRequestPermissionsResult(permissions, grantResults)
+ }
+}
+
+internal enum class FabStates(val iicon: IIcon, val iconColor: Int = Prefs.textColor, val backgroundTint: Int = Prefs.accentBackgroundColor.withAlpha(255)) {
+ ERROR(GoogleMaterial.Icon.gmd_error, Color.WHITE, Color.RED) {
+ override fun onClick(activity: ImageActivity) {
+ //todo add something
+ }
+ },
+ NOTHING(GoogleMaterial.Icon.gmd_adjust) {
+ override fun onClick(activity: ImageActivity) {}
+ },
+ DOWNLOAD(GoogleMaterial.Icon.gmd_file_download) {
+ override fun onClick(activity: ImageActivity) {
+ activity.downloadImage()
+ }
+ },
+ SHARE(GoogleMaterial.Icon.gmd_share) {
+ override fun onClick(activity: ImageActivity) {
+ try {
+ val photoURI = FileProvider.getUriForFile(activity,
+ BuildConfig.APPLICATION_ID + ".provider",
+ File(activity.downloadPath))
+ val intent = Intent(Intent.ACTION_SEND).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ putExtra(Intent.EXTRA_STREAM, photoURI)
+ type = "image/png"
+ }
+ activity.startActivity(intent)
+ } catch (e: Exception) {
+ L.e(e, "Image share failed");
+ activity.snackbar(R.string.image_share_failed)
+ }
+ }
+ };
+
+ /**
+ * Change the fab look
+ * If it's in view, give it some animations
+ */
+ fun update(fab: FloatingActionButton) {
+ if (!fab.isShown) {
+ fab.setIcon(iicon, color = iconColor)
+ fab.backgroundTintList = ColorStateList.valueOf(backgroundTint)
+ } else {
+ var switched = false
+ ValueAnimator.ofFloat(1.0f, 0.0f, 1.0f).apply {
+ duration = 500L
+ addUpdateListener {
+ val x = it.animatedValue as Float
+ val scale = x * 0.3f + 0.7f
+ fab.scaleX = scale
+ fab.scaleY = scale
+ fab.imageAlpha = (x * 255).toInt()
+ if (it.animatedFraction > 0.5f && !switched) {
+ switched = true
+ fab.setIcon(iicon, color = iconColor)
+ fab.backgroundTintList = ColorStateList.valueOf(backgroundTint)
+ }
+ }
+ start()
+ }
+ }
+ }
+
+ abstract fun onClick(activity: ImageActivity)
+
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt
index a27a1ee2..e4897be5 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.graphics.drawable.Drawable
import android.os.Bundle
@@ -16,6 +16,7 @@ import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.crashlytics.android.answers.LoginEvent
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.dbflow.fetchUsername
import com.pitchedapps.frost.dbflow.loadFbCookiesAsync
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
index f9a597db..ba76e594 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.app.AlarmManager
import android.app.PendingIntent
@@ -39,6 +39,8 @@ 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.BuildConfig
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.contracts.ActivityWebContract
import com.pitchedapps.frost.contracts.FileChooserContract
import com.pitchedapps.frost.contracts.FileChooserDelegate
@@ -107,7 +109,15 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract,
super.onCreate(savedInstanceState)
if (BuildConfig.VERSION_CODE > Prefs.versionCode) {
Prefs.versionCode = BuildConfig.VERSION_CODE
- if (!BuildConfig.DEBUG) showChangelog(R.xml.changelog, Prefs.textColor) { theme() }
+ if (!BuildConfig.DEBUG) {
+ showChangelog(R.xml.changelog, Prefs.textColor) { theme() }
+ frostAnswersCustom("Version") {
+ putCustomAttribute("Version code", BuildConfig.VERSION_CODE)
+ putCustomAttribute("Version name", BuildConfig.VERSION_NAME)
+ putCustomAttribute("Build type", BuildConfig.BUILD_TYPE)
+ putCustomAttribute("Frost id", Prefs.frostId)
+ }
+ }
}
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/SelectorActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt
index 103ae227..ff87f448 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/SelectorActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SelectorActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.os.Bundle
import android.support.constraint.ConstraintLayout
@@ -10,6 +10,7 @@ import ca.allanwang.kau.utils.bindView
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
import com.mikepenz.fastadapter.listeners.ClickEventHook
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.cookies
import com.pitchedapps.frost.utils.launchNewTask
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt
index e0be330d..b3b3bd7c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.content.Intent
import android.os.Bundle
@@ -13,6 +13,8 @@ import ca.allanwang.kau.ui.views.RippleCanvas
import ca.allanwang.kau.utils.*
import com.mikepenz.community_material_typeface_library.CommunityMaterial
import com.mikepenz.google_material_typeface_library.GoogleMaterial
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.settings.*
import com.pitchedapps.frost.utils.*
import com.pitchedapps.frost.utils.iab.*
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
index 6c1fb5bd..f03c653c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
@@ -1,4 +1,4 @@
-package com.pitchedapps.frost
+package com.pitchedapps.frost.activities
import android.content.Intent
import android.net.Uri
@@ -18,6 +18,7 @@ import ca.allanwang.kau.swipe.kauSwipeOnPostCreate
import ca.allanwang.kau.utils.*
import com.mikepenz.community_material_typeface_library.CommunityMaterial
import com.mikepenz.google_material_typeface_library.GoogleMaterial
+import com.pitchedapps.frost.R
import com.pitchedapps.frost.contracts.ActivityWebContract
import com.pitchedapps.frost.contracts.FileChooserContract
import com.pitchedapps.frost.contracts.FileChooserDelegate
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
index e5216881..4d5127c5 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
@@ -8,6 +8,6 @@ const val FACEBOOK_COM = "facebook.com"
const val FB_URL_BASE = "https://m.facebook.com/"
fun PROFILE_PICTURE_URL(id: Long) = "https://graph.facebook.com/$id/picture?type=large"
-const val USER_AGENT_BASIC_FULL = "Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900T Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36"
+const val USER_AGENT_FULL = "Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900T Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36"
const val USER_AGENT_BASIC = "Mozilla/5.0 (Linux; U; Android 2.3.3; en-gb; Nexus S Build/GRI20) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
const val USER_AGENT_MESSENGER = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
index e53bb202..622be067 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
@@ -14,10 +14,10 @@ class FbUrlFormatter(url: String) {
init {
var cleanedUrl = url
- discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "") }
+ discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
val changed = cleanedUrl != url //note that discardables strip away the first ?
- decoder.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v) }
- val qm = cleanedUrl.indexOf(if (changed)"&" else "?")
+ decoder.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
+ val qm = cleanedUrl.indexOf(if (changed) "&" else "?")
if (qm > -1) {
cleanedUrl.substring(qm + 1).split("&").forEach {
val p = it.split("=")
@@ -72,7 +72,15 @@ class FbUrlFormatter(url: String) {
"%60" to "`", "%3B" to ";", "%2F" to "/", "%3F" to "?",
"%3A" to ":", "%40" to "@", "%3D" to "=", "%26" to "&",
"%24" to "$", "%2B" to "+", "%22" to "\"", "%2C" to ",",
- "%20" to " "
+ "%20" to " ",
+ //css
+ "\\3C " to "<", "\\3E " to ">", "\\23 " to "#", "\\25 " to "%",
+ "\\7B " to "{", "\\7D " to "}", "\\7C " to "|", "\\5C " to "\\",
+ "\\5E " to "^", "\\7E " to "~", "\\5B " to "[", "\\5D " to "]",
+ "\\60 " to "`", "\\3B " to ";", "\\2F " to "/", "\\3F " to "?",
+ "\\3A " to ":", "\\40 " to "@", "\\3D " to "=", "\\26 " to "&",
+ "\\24 " to "$", "\\2B " to "+", "\\22 " to "\"", "\\2C " to ",",
+ "\\20 " to " "
)
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt
index 87afc434..239f5842 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt
@@ -7,7 +7,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import ca.allanwang.kau.utils.withArguments
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.FeedSort
import com.pitchedapps.frost.utils.Prefs
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 7b60a718..737b86ab 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
@@ -17,7 +17,7 @@ import ca.allanwang.kau.utils.string
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.pitchedapps.frost.BuildConfig
-import com.pitchedapps.frost.FrostWebActivity
+import com.pitchedapps.frost.activities.FrostWebActivity
import com.pitchedapps.frost.R
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.dbflow.fetchUsername
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt
index e05d0dd3..e814e1ac 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt
@@ -5,9 +5,9 @@ import ca.allanwang.kau.kpref.activity.items.KPrefColorPicker
import ca.allanwang.kau.kpref.activity.items.KPrefSeekbar
import ca.allanwang.kau.ui.views.RippleCanvas
import ca.allanwang.kau.utils.string
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.injectors.CssAssets
import com.pitchedapps.frost.utils.*
import com.pitchedapps.frost.utils.iab.IS_FROST_PRO
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt
index 7ce546b3..e7cf3598 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt
@@ -1,9 +1,9 @@
package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.Prefs
/**
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt
index 86bd356b..236d19f9 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt
@@ -1,9 +1,9 @@
package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.Showcase
@@ -20,10 +20,14 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = {
descRes = R.string.experimental_by_default_desc
}
+ // Experimental content starts here ------------------
+
checkbox(R.string.search_bar, { Prefs.searchBar }, { Prefs.searchBar = it; setResult(MainActivity.REQUEST_SEARCH) }) {
descRes = R.string.search_bar_desc
}
+ // Experimental content ends here --------------------
+
checkbox(R.string.verbose_logging, { Prefs.verboseLogging }, { Prefs.verboseLogging = it }) {
descRes = R.string.verbose_logging_desc
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
index 29256950..689e6bdb 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
@@ -2,9 +2,9 @@ package com.pitchedapps.frost.settings
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import ca.allanwang.kau.utils.string
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.facebook.FeedSort
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.materialDialogThemed
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
index b6105054..59ebf700 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt
@@ -4,7 +4,7 @@ import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
import ca.allanwang.kau.utils.minuteToText
import ca.allanwang.kau.utils.snackbar
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.services.fetchNotifications
import com.pitchedapps.frost.services.scheduleNotifications
import com.pitchedapps.frost.utils.Prefs
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
index d2e04898..f8c7af56 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
@@ -4,6 +4,7 @@ import ca.allanwang.kau.kotlin.lazyResettable
import ca.allanwang.kau.kpref.KPref
import ca.allanwang.kau.kpref.StringSet
import ca.allanwang.kau.kpref.kpref
+import ca.allanwang.kau.utils.colorToForeground
import ca.allanwang.kau.utils.isColorVisibleOn
import com.pitchedapps.frost.facebook.FeedSort
import com.pitchedapps.frost.injectors.InjectorContract
@@ -57,9 +58,24 @@ object Prefs : KPref() {
val iconColor: Int
get() = t.iconColor
+ /**
+ * Ensures that the color is visible against the background
+ */
val accentColor: Int
get() = if (headerColor.isColorVisibleOn(bgColor, 100)) headerColor else textColor
+ /**
+ * Ensures that the color is visible against both the foreground and background
+ */
+ val accentBackgroundColor: Int
+ get() {
+ if (headerColor.isColorVisibleOn(textColor, 100)) {
+ if (headerColor.isColorVisibleOn(bgColor, 100)) return headerColor
+ else return headerColor.colorToForeground(0.2f)
+ }
+ return bgColor.colorToForeground(0.2f)
+ }
+
val themeInjector: InjectorContract
get() = t.injector
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 9a599945..c0db8308 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -8,19 +8,27 @@ import android.support.annotation.StringRes
import android.support.design.internal.SnackbarContentLayout
import android.support.design.widget.Snackbar
import android.support.v7.widget.Toolbar
+import android.util.Log
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import ca.allanwang.kau.utils.*
import com.afollestad.materialdialogs.MaterialDialog
+import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.RequestBuilder
+import com.bumptech.glide.annotation.GlideExtension
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.module.AppGlideModule
import com.bumptech.glide.request.RequestOptions
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.answers.CustomEvent
-import com.pitchedapps.frost.*
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.activities.ImageActivity
+import com.pitchedapps.frost.activities.LoginActivity
+import com.pitchedapps.frost.activities.SelectorActivity
+import com.pitchedapps.frost.activities.WebOverlayActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.formattedFbUrl
@@ -31,6 +39,8 @@ import com.pitchedapps.frost.facebook.formattedFbUrl
const val EXTRA_COOKIES = "extra_cookies"
const val ARG_URL = "arg_url"
const val ARG_USER_ID = "arg_user_id"
+const val ARG_IMAGE_URL = "arg_image_url"
+const val ARG_TEXT = "arg_text"
@GlideModule
class FrostGlideModule : AppGlideModule()
@@ -59,6 +69,13 @@ fun Context.launchWebOverlay(url: String) {
})
}
+fun Context.launchImageActivity(imageUrl: String, text: String?) {
+ startActivity(ImageActivity::class.java, intentBuilder = {
+ putExtra(ARG_IMAGE_URL, imageUrl)
+ putExtra(ARG_TEXT, text)
+ })
+}
+
fun WebOverlayActivity.url(): String {
return intent.extras?.getString(ARG_URL) ?: FbTab.FEED.url
}
@@ -131,4 +148,4 @@ fun Activity.frostNavigationBar() {
navigationBarColor = if (Prefs.tintNavBar) Prefs.headerColor else Color.BLACK
}
-fun <T> RequestBuilder<T>.withRoundIcon() = apply(RequestOptions().transform(CircleCrop()))
+fun <T> RequestBuilder<T>.withRoundIcon() = apply(RequestOptions().transform(CircleCrop())) \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
index 67c20a5a..17ea46a3 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt
@@ -4,7 +4,8 @@ import android.content.Context
import ca.allanwang.kau.utils.copyToClipboard
import ca.allanwang.kau.utils.shareText
import ca.allanwang.kau.utils.string
-import com.pitchedapps.frost.MainActivity
+import ca.allanwang.kau.utils.toast
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
/**
@@ -18,7 +19,10 @@ fun Context.showWebContextMenu(wc: WebContext) {
materialDialogThemed {
title(title)
- items(WebContextType.values.map { this@showWebContextMenu.string(it.textId) })
+ items(WebContextType.values.map {
+ if (it == WebContextType.COPY_TEXT && wc.text == null) return@map null
+ this@showWebContextMenu.string(it.textId)
+ }.filterNotNull())
itemsCallback {
_, _, position, _ ->
WebContextType[position].onClick(this@showWebContextMenu, wc)
@@ -30,11 +34,11 @@ fun Context.showWebContextMenu(wc: WebContext) {
}
}
-class WebContext(val url: String, val text: String)
+class WebContext(val url: String, val text: String?)
enum class WebContextType(val textId: Int, val onClick: (c: Context, wc: WebContext) -> Unit) {
COPY_LINK(R.string.copy_link, { c, wc -> c.copyToClipboard(wc.url) }),
- COPY_TEXT(R.string.copy_text, { c, wc -> c.copyToClipboard(wc.text) }),
+ COPY_TEXT(R.string.copy_text, { c, wc -> if (wc.text != null) c.copyToClipboard(wc.text) else c.toast(R.string.no_text) }),
SHARE_LINK(R.string.share_link, { c, wc -> c.shareText(wc.url) })
;
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
index 58c748c2..964e771c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
@@ -9,7 +9,7 @@ import ca.allanwang.kau.utils.snackbar
import com.crashlytics.android.answers.PurchaseEvent
import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.*
/**
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
index 4f65b7f8..fae2f6bb 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
@@ -4,9 +4,9 @@ import android.app.Activity
import ca.allanwang.kau.utils.restart
import ca.allanwang.kau.utils.startPlayStoreLink
import ca.allanwang.kau.utils.string
-import com.pitchedapps.frost.MainActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.R
-import com.pitchedapps.frost.SettingsActivity
+import com.pitchedapps.frost.activities.SettingsActivity
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.materialDialogThemed
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt
new file mode 100644
index 00000000..09241254
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/BaseWebViewClient.kt
@@ -0,0 +1,16 @@
+package com.pitchedapps.frost.web
+
+import android.webkit.WebResourceRequest
+import android.webkit.WebResourceResponse
+import android.webkit.WebView
+import android.webkit.WebViewClient
+
+/**
+ * Created by Allan Wang on 2017-07-13.
+ */
+open class BaseWebViewClient : WebViewClient() {
+
+ override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse?
+ = shouldFrostInterceptRequest(view, request)
+
+} \ 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 aab3a165..4df6d6a7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt
@@ -1,9 +1,10 @@
package com.pitchedapps.frost.web
import android.net.Uri
-import android.os.Message
-import android.view.View
-import android.webkit.*
+import android.webkit.ConsoleMessage
+import android.webkit.ValueCallback
+import android.webkit.WebChromeClient
+import android.webkit.WebView
import ca.allanwang.kau.utils.snackbar
import com.pitchedapps.frost.contracts.ActivityWebContract
import com.pitchedapps.frost.utils.L
@@ -20,9 +21,16 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() {
val titleObservable: BehaviorSubject<String> = webCore.titleObservable
val activityContract = (webCore.context as? ActivityWebContract)
+ companion object {
+ val consoleBlacklist = setOf(
+ "edge-chat"
+ )
+ }
+
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
+ if (consoleBlacklist.any { consoleMessage.message().contains(it) }) return true
L.i("Chrome Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}")
- return super.onConsoleMessage(consoleMessage)
+ return true
}
override fun onReceivedTitle(view: WebView, title: String) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
index 3340e7d2..3f976fb8 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -2,8 +2,9 @@ package com.pitchedapps.frost.web
import android.content.Context
import android.webkit.JavascriptInterface
-import ca.allanwang.kau.logging.KL
-import com.pitchedapps.frost.MainActivity
+import ca.allanwang.kau.utils.startActivity
+import com.pitchedapps.frost.activities.ImageActivity
+import com.pitchedapps.frost.activities.MainActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.utils.*
@@ -13,12 +14,18 @@ import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-06-01.
*/
-class FrostJSI(val context: Context, val webView: FrostWebViewCore) {
+class FrostJSI(val webView: FrostWebViewCore) {
- val headerObservable: Subject<String>? = (context as? MainActivity)?.headerBadgeObservable
+ val context: Context
+ get() = webView.context
+
+ val activity: MainActivity?
+ get() = (context as? MainActivity)
+
+ val headerObservable: Subject<String>? = activity?.headerBadgeObservable
val cookies: ArrayList<CookieModel>
- get() = (context as? MainActivity)?.cookies() ?: arrayListOf()
+ get() = activity?.cookies() ?: arrayListOf()
@JavascriptInterface
fun loadUrl(url: String) {
@@ -35,8 +42,8 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore) {
}
@JavascriptInterface
- fun contextMenu(url: String, text: String) {
- webView.post { webView.context.showWebContextMenu(WebContext(url.formattedFbUrl, text)) }
+ fun contextMenu(url: String, text: String?) {
+ webView.post { context.showWebContextMenu(WebContext(url.formattedFbUrl, text)) }
}
/**
@@ -45,7 +52,7 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore) {
*/
@JavascriptInterface
fun longClick(start: Boolean) {
- (webView.context as? MainActivity)?.viewPager?.enableSwipe = !start
+ activity?.viewPager?.enableSwipe = !start
}
@JavascriptInterface
@@ -53,6 +60,14 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore) {
context.launchLogin(cookies, true)
}
+ /**
+ * Launch image overlay
+ */
+ @JavascriptInterface
+ fun loadImage(imageUrl: String, text: String?) {
+ context.launchImageActivity(imageUrl, text)
+ }
+
@JavascriptInterface
fun emit(flag: Int) {
webView.post { webView.frostWebClient.emit(flag) }
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt
new file mode 100644
index 00000000..45dc83aa
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostRequestInterceptor.kt
@@ -0,0 +1,67 @@
+package com.pitchedapps.frost.web
+
+import android.graphics.Bitmap.CompressFormat
+import android.webkit.WebResourceRequest
+import android.webkit.WebResourceResponse
+import android.webkit.WebView
+import ca.allanwang.kau.utils.use
+import com.pitchedapps.frost.utils.GlideApp
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.Prefs
+import okhttp3.HttpUrl
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+
+
+/**
+ * Created by Allan Wang on 2017-07-13.
+ *
+ * Handler to decide when a request should be done by us
+ * This is the crux of Frost's optimizations for the web browser
+ */
+val blankResource: WebResourceResponse by lazy { WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray())) }
+
+//these hosts will redirect to a blank resource
+val blacklistHost: Set<String> by lazy {
+ setOf(
+ "edge-chat.facebook.com"
+ )
+}
+
+//these hosts will return null and skip logging
+val whitelistHost: Set<String> by lazy {
+ setOf(
+ "static.xx.fbcdn.net",
+ "m.facebook.com",
+ "touch.facebook.com"
+ )
+}
+
+//these hosts will skip ad inspection
+//this list does not have to include anything from the two above
+val adWhitelistHost: Set<String> by lazy {
+ setOf(
+ "scontent-sea1-1.xx.fbcdn.net"
+ )
+}
+
+var adblock: Set<String>? = null
+
+fun shouldFrostInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
+ val httpUrl = HttpUrl.parse(request.url?.toString() ?: return null) ?: return null
+ val host = httpUrl.host()
+ val url = httpUrl.toString()
+ if (blacklistHost.contains(host)) return blankResource
+ if (whitelistHost.contains(host)) return null
+ if (!adWhitelistHost.contains(host)) {
+ if (adblock == null) adblock = view.context.assets.open("adblock.txt").bufferedReader().use { it.readLines().toSet() }
+ if (adblock?.any { url.contains(it) } ?: false) return blankResource
+ }
+ L.v("Intercept Request ${host} ${url}")
+ return null
+}
+
+fun WebResourceResponse?.filterCss(request: WebResourceRequest): WebResourceResponse?
+ = this ?: if (request.url.path.endsWith(".css")) blankResource else null
+
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
index 7c0a6597..e7dae22a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
@@ -69,7 +69,7 @@ class FrostWebView @JvmOverloads constructor(
frostWebClient = baseEnum?.webClient?.invoke(this) ?: FrostWebViewClient(this)
webViewClient = frostWebClient
webChromeClient = FrostChromeClient(this)
- addJavascriptInterface(FrostJSI(context, this), "Frost")
+ addJavascriptInterface(FrostJSI(this), "Frost")
setBackgroundColor(Color.TRANSPARENT)
}
}
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 5e5dc597..5b2b4bfd 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt
@@ -4,10 +4,9 @@ import android.content.Context
import android.graphics.Bitmap
import android.webkit.WebResourceRequest
import android.webkit.WebView
-import android.webkit.WebViewClient
-import com.pitchedapps.frost.LoginActivity
-import com.pitchedapps.frost.MainActivity
-import com.pitchedapps.frost.SelectorActivity
+import com.pitchedapps.frost.activities.LoginActivity
+import com.pitchedapps.frost.activities.MainActivity
+import com.pitchedapps.frost.activities.SelectorActivity
import com.pitchedapps.frost.facebook.FACEBOOK_COM
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.injectors.*
@@ -17,7 +16,7 @@ import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
-open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() {
+open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient() {
val refreshObservable: Subject<Boolean> = webCore.refreshObservable
@@ -97,15 +96,9 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() {
return super.shouldOverrideUrlLoading(view, request)
}
- override fun onPageCommitVisible(view: WebView?, url: String?) {
- L.d("ASDF PCV")
- super.onPageCommitVisible(view, url)
- }
-
-// override fun onLoadResource(view: WebView, url: String) {
-// L.v("Load resource $url")
-// super.onLoadResource(view, url)
+// override fun onPageCommitVisible(view: WebView?, url: String?) {
+// L.d("ASDF PCV")
+// super.onPageCommitVisible(view, url)
// }
-
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
index 1e023dca..d96fba55 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
@@ -72,7 +72,7 @@ class FrostWebViewCore @JvmOverloads constructor(
dispose = refreshObservable.subscribeOn(AndroidSchedulers.mainThread()).subscribe {
if (it) {
loading = true
- if (isVisible()) fadeOut(duration = 200L)
+ if (isVisible) fadeOut(duration = 200L)
} else if (loading) {
dispose?.dispose()
if (animate && Prefs.animate) circularReveal(offset = 150L)
@@ -148,11 +148,12 @@ class FrostWebViewCore @JvmOverloads constructor(
if (scrollY > 10000) {
scrollTo(0, 0)
} else {
- val animator = ValueAnimator.ofInt(scrollY, 0)
- animator.duration = Math.min(scrollY, 500).toLong()
- animator.interpolator = DecelerateInterpolator()
- animator.addUpdateListener { scrollY = it.animatedValue as Int }
- animator.start()
+ ValueAnimator.ofInt(scrollY, 0).apply {
+ duration = Math.min(scrollY, 500).toLong()
+ interpolator = DecelerateInterpolator()
+ addUpdateListener { scrollY = it.animatedValue as Int }
+ start()
+ }
}
}
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 b3b1cfe5..bcadf32a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt
@@ -3,9 +3,7 @@ package com.pitchedapps.frost.web
import android.annotation.SuppressLint
import android.content.Context
import android.view.View
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
+import android.webkit.*
import ca.allanwang.kau.searchview.SearchItem
import ca.allanwang.kau.utils.gone
import com.pitchedapps.frost.facebook.FbTab
@@ -56,7 +54,8 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi
settings.javaScriptEnabled = true
settings.userAgentString = USER_AGENT_BASIC
setLayerType(View.LAYER_TYPE_HARDWARE, null)
- webViewClient = FrostWebViewClientSearch()
+ webViewClient = SearchWebViewClient()
+ webChromeClient = SearchChromeClient()
addJavascriptInterface(SearchJSI(), "Frost")
searchSubject.debounce(300, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.newThread())
.map {
@@ -111,13 +110,22 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi
*
* Barebones client that does what [FrostWebViewSearch] needs
*/
- inner class FrostWebViewClientSearch : WebViewClient() {
+ inner class SearchWebViewClient : BaseWebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
L.i("Search Page finished $url")
view.jsInject(JsAssets.SEARCH)
}
+
+ override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse?
+ = super.shouldInterceptRequest(view, request).filterCss(request)
+ }
+
+ class SearchChromeClient : WebChromeClient() {
+
+ //mute console
+ override fun onConsoleMessage(consoleMessage: ConsoleMessage) = true
}
inner class SearchJSI {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
index 38d4ad8c..d7a2db0a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
@@ -66,7 +66,7 @@ class LoginWebView @JvmOverloads constructor(
}
- inner class LoginClient : WebViewClient() {
+ inner class LoginClient : BaseWebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
@@ -88,7 +88,7 @@ class LoginWebView @JvmOverloads constructor(
inner class LoginChromeClient : WebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
L.d("Login Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}")
- return super.onConsoleMessage(consoleMessage)
+ return true
}
override fun onProgressChanged(view: WebView, newProgress: Int) {