From b10a745c7f0f46f4f014e1ba7fa71172d7442b83 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sat, 8 Jul 2017 03:03:55 -0400 Subject: Dev-1.1.7 (#39) - feature overload + context menu * Address some crashlytics issues * Add text scaling * Kau fixes and cleanup * WIP formatter * Create in house url formatter * Update context menu * Update themes * Test proguard without R * Implement sharing and clean up context menu * Disable viewpager swipe on long press * Test keeping lib strings * Update changelog and proguard --- .../kotlin/com/pitchedapps/frost/AboutActivity.kt | 62 +++++++------ .../com/pitchedapps/frost/FrostWebActivity.kt | 1 - .../kotlin/com/pitchedapps/frost/MainActivity.kt | 10 ++- .../com/pitchedapps/frost/WebOverlayActivity.kt | 10 +-- .../com/pitchedapps/frost/dbflow/CookiesDb.kt | 4 +- .../pitchedapps/frost/facebook/FbUrlFormatter.kt | 76 ++++++++++++++++ .../com/pitchedapps/frost/fragments/WebFragment.kt | 5 +- .../com/pitchedapps/frost/settings/Appearance.kt | 7 ++ .../com/pitchedapps/frost/settings/Behaviour.kt | 12 ++- .../kotlin/com/pitchedapps/frost/utils/Prefs.kt | 4 + .../kotlin/com/pitchedapps/frost/utils/Utils.kt | 16 +--- .../com/pitchedapps/frost/utils/WebContextMenu.kt | 45 ++++++++++ .../kotlin/com/pitchedapps/frost/utils/iab/IAB.kt | 5 +- .../com/pitchedapps/frost/views/AccountItem.kt | 11 +-- .../com/pitchedapps/frost/views/FrostViewPager.kt | 21 +++++ .../pitchedapps/frost/views/KPrefTextSeekbar.kt | 47 ++++++++++ .../kotlin/com/pitchedapps/frost/web/FrostJSI.kt | 21 +++-- .../pitchedapps/frost/web/FrostWebContextMenu.kt | 100 --------------------- .../com/pitchedapps/frost/web/FrostWebView.kt | 6 +- .../pitchedapps/frost/web/FrostWebViewClient.kt | 2 +- .../com/pitchedapps/frost/web/FrostWebViewCore.kt | 6 +- .../pitchedapps/frost/web/FrostWebViewSearch.kt | 6 +- 22 files changed, 289 insertions(+), 188 deletions(-) create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebContextMenu.kt (limited to 'app/src/main/kotlin/com') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt index 7aa4eac3..36809535 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt @@ -1,10 +1,10 @@ package com.pitchedapps.frost -import android.graphics.Color import android.support.constraint.ConstraintLayout import android.support.constraint.ConstraintSet import android.support.v7.widget.RecyclerView import android.view.View +import android.view.ViewGroup import android.widget.ImageView import ca.allanwang.kau.about.AboutActivityBase import ca.allanwang.kau.adapters.FastItemThemedAdapter @@ -12,7 +12,6 @@ import ca.allanwang.kau.adapters.ThemableIItem import ca.allanwang.kau.adapters.ThemableIItemDelegate import ca.allanwang.kau.iitems.LibraryIItem import ca.allanwang.kau.utils.* -import ca.allanwang.kau.views.createSimpleRippleDrawable import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.entity.License @@ -27,7 +26,7 @@ import com.pitchedapps.frost.utils.Prefs /** * Created by Allan Wang on 2017-06-26. */ -class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { +class AboutActivity : AboutActivityBase(null, { textColor = Prefs.textColor accentColor = Prefs.accentColor backgroundColor = Prefs.bgColor.withMinAlpha(200) @@ -37,10 +36,16 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { override fun getLibraries(libs: Libs): List { val include = arrayOf( - "materialdialogs", - "kotterknife", + "AboutLibraries", + "AndroidIconics", + "dbflow", + "fastadapter", "glide", - "jsoup" + "Jsoup", + "kau", + "kotterknife", + "materialdialogs", + "materialdrawer" ) /* @@ -58,7 +63,7 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { "recyclerview_v7", "support_v4" ) - val l = libs.prepareLibraries(this, include, exclude, true, true) + val l = libs.prepareLibraries(this, include, null, false, true) // l.forEach { KL.d("Lib ${it.definedName}") } return l } @@ -93,7 +98,7 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { override fun bindView(holder: ViewHolder, payloads: MutableList?) { super.bindView(holder, payloads) with(holder) { - bindIconColor(*icons.toTypedArray()) + bindIconColor(*images.toTypedArray()) bindBackgroundColor(container) } } @@ -101,7 +106,7 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { val container: ConstraintLayout by bindView(R.id.about_icons_container) - val icons: List + val images: List /** * There are a lot of constraints to be added to each item just to have them chained properly @@ -111,35 +116,28 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { */ init { val c = itemView.context - val iconData = arrayOf, () -> Unit>>( - R.id.about_rate to GoogleMaterial.Icon.gmd_star to { c.startPlayStoreLink(R.string.play_store_package_id) }, - R.id.about_reddit to CommunityMaterial.Icon.cmd_reddit to { c.startLink("https://www.reddit.com/r/FrostForFacebook/") }, - R.id.about_github to CommunityMaterial.Icon.cmd_github_circle to { c.startLink("https://github.com/AllanWang/Frost-for-Facebook") } - ).map { - //decouple and setup, then only return the image - (pair, onClick) -> - val (id, icon: IIcon) = pair - val image = itemView.findViewById(id) - setup(image, icon, onClick) - Pair(image, id) + val size = c.dimenPixelSize(R.dimen.kau_avatar_bounds) + images = arrayOf Unit>>( + GoogleMaterial.Icon.gmd_star to { c.startPlayStoreLink(R.string.play_store_package_id) }, + CommunityMaterial.Icon.cmd_reddit to { c.startLink("https://www.reddit.com/r/FrostForFacebook/") }, + CommunityMaterial.Icon.cmd_github_circle to { c.startLink("https://github.com/AllanWang/Frost-for-Facebook") } + ).mapIndexed { i, (icon, onClick) -> + ImageView(c).apply { + layoutParams = ViewGroup.LayoutParams(size, size) + id = 109389 + i + setImageDrawable(icon.toDrawable(context, 32)) + scaleType = ImageView.ScaleType.CENTER + background = context.resolveDrawable(android.R.attr.selectableItemBackgroundBorderless) + setOnClickListener({ onClick() }) + container.addView(this) + } } - icons = iconData.map { it.first } - val ids = iconData.map { it.second }.toIntArray() val set = ConstraintSet() set.clone(container) set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT, - ids, null, ConstraintSet.CHAIN_SPREAD_INSIDE) + images.map { it.id }.toIntArray(), null, ConstraintSet.CHAIN_SPREAD_INSIDE) set.applyTo(container) } - - fun setup(image: ImageView, icon: IIcon, onClick: () -> Unit) { - with(image) { - setImageDrawable(icon.toDrawable(context, 32)) - scaleType = ImageView.ScaleType.CENTER - background = context.resolveDrawable(android.R.attr.selectableItemBackgroundBorderless) - setOnClickListener({ onClick() }) - } - } } } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt index 98b5897f..3e337813 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostWebActivity.kt @@ -2,7 +2,6 @@ package com.pitchedapps.frost import android.os.Bundle import com.pitchedapps.frost.utils.Prefs -import com.pitchedapps.frost.utils.formattedFbUrl /** diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index 592c3b5b..f9a597db 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -52,6 +52,7 @@ import com.pitchedapps.frost.fragments.WebFragment import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.utils.iab.validatePro import com.pitchedapps.frost.views.BadgedIcon +import com.pitchedapps.frost.views.FrostViewPager import com.pitchedapps.frost.web.FrostWebViewSearch import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -65,7 +66,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, lateinit var adapter: SectionsPagerAdapter val toolbar: Toolbar by bindView(R.id.toolbar) - val viewPager: ViewPager by bindView(R.id.container) + val viewPager: FrostViewPager by bindView(R.id.container) val fab: FloatingActionButton by bindView(R.id.fab) val tabs: TabLayout by bindView(R.id.tabs) val appBar: AppBarLayout by bindView(R.id.appbar) @@ -89,7 +90,6 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, get() = searchView?.isOpen ?: false companion object { - const val FRAGMENT_REFRESH = 99 const val ACTIVITY_SETTINGS = 97 /* * Possible responses from the SettingsActivity @@ -97,6 +97,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, */ const val REQUEST_RESTART = 90909 const val REQUEST_REFRESH = 80808 + const val REQUEST_WEB_ZOOM = 50505 const val REQUEST_NAV = 10101 const val REQUEST_SEARCH = 70707 const val REQUEST_RESTART_APPLICATION = 60606 @@ -333,7 +334,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, } fun refreshAll() { - webFragmentObservable.onNext(FRAGMENT_REFRESH) + webFragmentObservable.onNext(WebFragment.REQUEST_REFRESH) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -384,8 +385,9 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract, if (requestCode == ACTIVITY_SETTINGS) { when (resultCode) { REQUEST_RESTART -> restart() - REQUEST_REFRESH -> webFragmentObservable.onNext(FRAGMENT_REFRESH) + REQUEST_REFRESH -> webFragmentObservable.onNext(WebFragment.REQUEST_REFRESH) REQUEST_NAV -> frostNavigationBar() + REQUEST_WEB_ZOOM -> webFragmentObservable.onNext(WebFragment.REQUEST_TEXT_ZOOM) REQUEST_SEARCH -> invalidateOptionsMenu() REQUEST_RESTART_APPLICATION -> { //completely restart application L.d("Restart Application Requested") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt index c256b674..dda6c066 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt @@ -22,6 +22,7 @@ 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.facebook.formattedFbUrl import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.web.FrostWebView @@ -80,7 +81,7 @@ open class WebOverlayActivity : AppCompatActivity(), */ override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - val newUrl = (intent.extras?.getString(ARG_URL) ?: intent.dataString).formattedFbUrl + val newUrl = (intent.extras?.getString(ARG_URL) ?: intent.dataString ?: return).formattedFbUrl L.d("New intent") if (url != newUrl) { this.intent = intent @@ -143,12 +144,7 @@ open class WebOverlayActivity : AppCompatActivity(), override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_copy_link -> copyToClipboard(frostWeb.web.url) - R.id.action_share -> { - val intent = Intent(Intent.ACTION_SEND) - intent.type = "text/plain" - intent.putExtra(Intent.EXTRA_TEXT, frostWeb.web.url) - startActivity(Intent.createChooser(intent, string(R.string.share))) - } + R.id.action_share -> shareText(frostWeb.web.url) else -> return super.onOptionsItemSelected(item) } return true diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt index 932324f2..901ba02d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt @@ -14,6 +14,7 @@ import com.raizlabs.android.dbflow.structure.BaseModel import org.jetbrains.anko.doAsync import org.jsoup.Jsoup import paperparcel.PaperParcel +import java.net.UnknownHostException /** * Created by Allan Wang on 2017-05-30. @@ -71,7 +72,8 @@ fun CookieModel.fetchUsername(callback: (String) -> Unit) { .get().title() L.d("Fetch username found", result) } catch (e: Exception) { - L.e(e, "Fetch username failed") + if (e !is UnknownHostException) + L.e(e, "Fetch username failed") } finally { if (result.isBlank() && (name?.isNotBlank() ?: false)) { callback(name!!) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt new file mode 100644 index 00000000..1090f1f3 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt @@ -0,0 +1,76 @@ +package com.pitchedapps.frost.facebook + +/** + * Created by Allan Wang on 2017-07-07. + * + * Custom url builder so we can easily test it without the Android framework + */ +val String.formattedFbUrl: String + get() = FbUrlFormatter(this).toString() + +class FbUrlFormatter(url: String) { + val queries = mutableMapOf() + val cleaned: String + + init { + var cleanedUrl = url + discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "") } + decoder.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v) } + val qm = cleanedUrl.indexOf("?") + if (qm > -1) { + cleanedUrl.substring(qm + 1).split("&").forEach { + val p = it.split("=") + queries.put(p[0], p.elementAtOrNull(1) ?: "") + } + cleanedUrl = cleanedUrl.substring(0, qm) + } + discardableQueries.forEach { queries.remove(it) } + if (cleanedUrl.startsWith("#!/")) cleanedUrl = cleanedUrl.substring(2) + if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1) + cleaned = cleanedUrl + } + + override fun toString(): String { + val builder = StringBuilder() + builder.append(cleaned) + if (queries.isNotEmpty()) { + builder.append("?") + queries.forEach { (k, v) -> builder.append("$k=$v&") } + } + return builder.removeSuffix("&").toString() + } + + fun toLogList(): List { + val list = mutableListOf(cleaned) + queries.forEach { (k, v) -> list.add("- $k\t=\t$v") } + return list + } + + companion object { + /** + * Items here are explicitly removed from the url + * Taken from FaceSlim + * https://github.com/indywidualny/FaceSlim/blob/master/app/src/main/java/org/indywidualni/fblite/util/Miscellany.java + */ + @JvmStatic val discardable = arrayOf( + "http://lm.facebook.com/l.php?u=", + "https://lm.facebook.com/l.php?u=", + "http://m.facebook.com/l.php?u=", + "https://m.facebook.com/l.php?u=", + "http://touch.facebook.com/l.php?u=", + "https://touch.facebook.com/l.php?u=" + ) + + @JvmStatic val discardableQueries = arrayOf("ref", "refid") + + @JvmStatic val decoder = mapOf( + "%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 764c0bc9..87afc434 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragment.kt @@ -27,6 +27,8 @@ class WebFragment : Fragment() { private const val ARG_URL = "arg_url" private const val ARG_URL_ENUM = "arg_url_enum" private const val ARG_POSITION = "arg_position" + const val REQUEST_TEXT_ZOOM = 17 + const val REQUEST_REFRESH = 99 operator fun invoke(data: FbTab, position: Int) = WebFragment().withArguments( ARG_URL to data.url, @@ -96,7 +98,7 @@ class WebFragment : Fragment() { * Flags between -10 and 10 are reserved for viewpager events */ when (it) { - MainActivity.FRAGMENT_REFRESH -> { + REQUEST_REFRESH -> { web.clearHistory() web.loadBaseUrl(true) } @@ -107,6 +109,7 @@ class WebFragment : Fragment() { -(position + 1) -> { //we are moving away from this fragment if (!frostWebView.refresh.isRefreshing) pauseLoad = true } + REQUEST_TEXT_ZOOM -> frostWebView.web.settings.textZoom = Prefs.webTextScaling } } } 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 135e621f..68d3fbc6 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt @@ -2,6 +2,7 @@ package com.pitchedapps.frost.settings import ca.allanwang.kau.kpref.KPrefAdapterBuilder import ca.allanwang.kau.kpref.items.KPrefColorPicker +import ca.allanwang.kau.kpref.items.KPrefSeekbar import ca.allanwang.kau.utils.string import ca.allanwang.kau.views.RippleCanvas import com.pitchedapps.frost.MainActivity @@ -11,6 +12,7 @@ import com.pitchedapps.frost.injectors.CssAssets import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.utils.iab.IS_FROST_PRO import com.pitchedapps.frost.utils.iab.openPlayProPurchase +import com.pitchedapps.frost.views.KPrefTextSeekbar /** * Created by Allan Wang on 2017-06-29. @@ -119,4 +121,9 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = { }) { descRes = R.string.tint_nav_desc } + + list.add(KPrefTextSeekbar( + KPrefSeekbar.KPrefSeekbarBuilder( + globalOptions, + R.string.web_text_scaling, { Prefs.webTextScaling }, { Prefs.webTextScaling = it; setResult(MainActivity.REQUEST_WEB_ZOOM) }))) } \ No newline at end of file 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 ae7fd061..2d121073 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt @@ -14,6 +14,14 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.fancy_animations_desc } + checkbox(R.string.overlay_full_screen_swipe, { Prefs.overlayFullScreenSwipe }, { Prefs.overlayFullScreenSwipe = it }) { + descRes = R.string.overlay_full_screen_swipe_desc + } + + checkbox(R.string.viewpager_swipe, { Prefs.viewpagerSwipe }, { Prefs.viewpagerSwipe = it }) { + descRes = R.string.viewpager_swipe_desc + } + checkbox(R.string.exit_confirmation, { Prefs.exitConfirmation }, { Prefs.exitConfirmation = it }) { descRes = R.string.exit_confirmation_desc } @@ -22,8 +30,4 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { descRes = R.string.analytics_desc } - checkbox(R.string.overlay_full_screen_swipe, { Prefs.overlayFullScreenSwipe }, { Prefs.overlayFullScreenSwipe = it }) { - descRes = R.string.overlay_full_screen_swipe_desc - } - } 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 7b5eca2a..95642a7a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -71,6 +71,8 @@ object Prefs : KPref() { var tintNavBar: Boolean by kpref("tint_nav_bar", true) + var webTextScaling: Int by kpref("web_text_scaling", 100) + var feedSort: Int by kpref("feed_sort", FeedSort.DEFAULT.ordinal) var showRoundedIcons: Boolean by kpref("rounded_icons", true) @@ -101,4 +103,6 @@ object Prefs : KPref() { var overlayFullScreenSwipe: Boolean by kpref("overlay_full_screen_swipe", true) + var viewpagerSwipe: Boolean by kpref("viewpager_swipe", true) + } 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 9fee3571..6f3459d7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -23,8 +23,8 @@ import com.crashlytics.android.answers.Answers import com.crashlytics.android.answers.CustomEvent import com.pitchedapps.frost.* import com.pitchedapps.frost.dbflow.CookieModel -import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.FbTab +import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.services.NotificationService /** @@ -51,17 +51,6 @@ fun Activity.cookies(): ArrayList { return intent?.extras?.getParcelableArrayList(EXTRA_COOKIES) ?: arrayListOf() } -val String.formattedFbUrl: String - get() { - var url = this - if (url.startsWith("#!/")) url = url.substring(2) - if (url.startsWith('/')) url = FB_URL_BASE + url.substring(1) - url = url.replace("/#!/", "/") - val ref = url.indexOf("?ref") - if (ref != -1) url = url.substring(0, ref) - return url - } - fun Context.launchWebOverlay(url: String) { val argUrl = url.formattedFbUrl L.v("Launch received $url") @@ -172,4 +161,5 @@ fun View.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {}) fun Activity.frostNavigationBar() { navigationBarColor = if (Prefs.tintNavBar) Prefs.headerColor else Color.BLACK -} \ 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 new file mode 100644 index 00000000..67c20a5a --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/WebContextMenu.kt @@ -0,0 +1,45 @@ +package com.pitchedapps.frost.utils + +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 com.pitchedapps.frost.R + +/** + * Created by Allan Wang on 2017-07-07. + */ +fun Context.showWebContextMenu(wc: WebContext) { + + var title = wc.url + title = title.substring(title.indexOf("m/") + 1) //just so if defaults to 0 in case it's not .com/ + if (title.length > 100) title = title.substring(0, 100) + '\u2026' + + materialDialogThemed { + title(title) + items(WebContextType.values.map { this@showWebContextMenu.string(it.textId) }) + itemsCallback { + _, _, position, _ -> + WebContextType[position].onClick(this@showWebContextMenu, wc) + } + dismissListener { + //showing the dialog interrupts the touch down event, so we must ensure that the viewpager's swipe is enabled + (this@showWebContextMenu as? MainActivity)?.viewPager?.enableSwipe = true + } + } +} + +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) }), + SHARE_LINK(R.string.share_link, { c, wc -> c.shareText(wc.url) }) + ; + + companion object { + val values = values() + operator fun get(index: Int) = values[index] + } +} \ No newline at end of file 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 a2dc82f3..21326efa 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 @@ -170,7 +170,10 @@ fun Activity.getInventory( helper.queryInventoryAsync { res, inv -> L.d("Inventory query finished") - if (res.isFailure || inv == null) onFailed() + if (res.isFailure || inv == null) { + L.e("Res error ${res.message}") + onFailed() + } else onSuccess(inv, helper) } } 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 c9ee5a76..0fb9dbbb 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt @@ -5,6 +5,7 @@ import android.support.v7.widget.AppCompatTextView import android.support.v7.widget.RecyclerView import android.view.View import android.widget.ImageView +import ca.allanwang.kau.iitems.KauIItem import ca.allanwang.kau.utils.bindView import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.toDrawable @@ -15,7 +16,6 @@ import com.bumptech.glide.load.resource.bitmap.CircleCrop import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target -import com.mikepenz.fastadapter.items.AbstractItem import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.pitchedapps.frost.R import com.pitchedapps.frost.dbflow.CookieModel @@ -25,13 +25,8 @@ import com.pitchedapps.frost.utils.Prefs /** * Created by Allan Wang on 2017-06-05. */ -class AccountItem(val cookie: CookieModel?) : AbstractItem() { - - override fun getType(): Int = R.id.item_account - - override fun getViewHolder(v: View) = ViewHolder(v) - - override fun getLayoutRes(): Int = R.layout.view_account +class AccountItem(val cookie: CookieModel?) : KauIItem +(R.layout.view_account, { ViewHolder(it) }, R.id.item_account) { override fun bindView(viewHolder: ViewHolder, payloads: List?) { super.bindView(viewHolder, payloads) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt new file mode 100644 index 00000000..b856f973 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt @@ -0,0 +1,21 @@ +package com.pitchedapps.frost.views + +import android.content.Context +import android.support.v4.view.ViewPager +import android.util.AttributeSet +import android.view.MotionEvent +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.Prefs + +/** + * Created by Allan Wang on 2017-07-07. + * + * Basic override to allow us to control swiping + */ +class FrostViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) { + var enableSwipe = true + + override fun onInterceptTouchEvent(ev: MotionEvent?) = Prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev) + + override fun onTouchEvent(ev: MotionEvent?): Boolean = Prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev) +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt new file mode 100644 index 00000000..1d8f308d --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt @@ -0,0 +1,47 @@ +package com.pitchedapps.frost.views + +import android.annotation.SuppressLint +import android.util.TypedValue +import ca.allanwang.kau.kpref.items.KPrefSeekbar +import com.pitchedapps.frost.R + +/** + * Created by Allan Wang on 2017-07-07. + */ +class KPrefTextSeekbar(builder: KPrefSeekbarContract) : KPrefSeekbar(builder) { + + var descOriginalSize = 1f + + init { + with(builder) { + min = 50 + max = 200 + descRes = R.string.web_text_scaling_desc + textViewConfigs = { + minEms = 2 + setOnLongClickListener { + pref = 100 + reloadSelf() + true + } + } + } + } + + @SuppressLint("MissingSuperCall") + override fun onPostBindView(viewHolder: ViewHolder, textColor: Int?, accentColor: Int?) { + descOriginalSize = viewHolder.desc?.textSize ?: 1f + viewHolder.desc?.layoutParams + builder.toText = { + viewHolder.desc?.setTextSize(TypedValue.COMPLEX_UNIT_PX, descOriginalSize * it.toFloat() / 100) + "$it%" + } + + super.onPostBindView(viewHolder, textColor, accentColor) + } + + override fun unbindView(holder: ViewHolder) { + holder.desc?.setTextSize(TypedValue.COMPLEX_UNIT_PX, descOriginalSize) + super.unbindView(holder) + } +} \ No newline at end of file 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 68163333..3340e7d2 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,10 @@ 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 com.pitchedapps.frost.dbflow.CookieModel +import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.utils.* import io.reactivex.subjects.Subject @@ -11,7 +13,7 @@ import io.reactivex.subjects.Subject /** * Created by Allan Wang on 2017-06-01. */ -class FrostJSI(val context: Context, val webView: FrostWebViewCore, val contextMenu: FrostWebContextMenu) { +class FrostJSI(val context: Context, val webView: FrostWebViewCore) { val headerObservable: Subject? = (context as? MainActivity)?.headerBadgeObservable @@ -33,8 +35,17 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore, val contextM } @JavascriptInterface - fun contextMenu(url: String) { - contextMenu.post { contextMenu.show(url) } + fun contextMenu(url: String, text: String) { + webView.post { webView.context.showWebContextMenu(WebContext(url.formattedFbUrl, text)) } + } + + /** + * Get notified when a stationary long click starts or ends + * This will be used to toggle the main activities viewpager swipe + */ + @JavascriptInterface + fun longClick(start: Boolean) { + (webView.context as? MainActivity)?.viewPager?.enableSwipe = !start } @JavascriptInterface @@ -44,12 +55,12 @@ class FrostJSI(val context: Context, val webView: FrostWebViewCore, val contextM @JavascriptInterface fun emit(flag: Int) { - webView.post { webView.frostWebClient!!.emit(flag) } + webView.post { webView.frostWebClient.emit(flag) } } @JavascriptInterface fun handleHtml(html: String) { - webView.post { webView.frostWebClient!!.handleHtml(html) } + webView.post { webView.frostWebClient.handleHtml(html) } } @JavascriptInterface diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebContextMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebContextMenu.kt deleted file mode 100644 index e0aa5ebd..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebContextMenu.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.pitchedapps.frost.web - -import android.content.Context -import android.support.constraint.ConstraintLayout -import android.support.constraint.ConstraintSet -import android.text.method.ScrollingMovementMethod -import android.util.AttributeSet -import android.widget.TextView -import ca.allanwang.kau.logging.KL -import ca.allanwang.kau.utils.* -import com.pitchedapps.frost.R -import com.pitchedapps.frost.utils.Prefs - -/** - * Created by Allan Wang on 2017-07-06. - */ -class FrostWebContextMenu @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr) { - - var url = "" - - val urlHolder = TextView(context, attrs, defStyleAttr) - - init { - layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) - elevation = 20f - setBackgroundColor(0x80000000.toInt()) - gone() - - val tc = Prefs.textColor - val bg = Prefs.bgColor.colorToForeground(0.1f).withAlpha(255) - - urlHolder.apply { - isVerticalScrollBarEnabled = true - movementMethod = ScrollingMovementMethod() - maxHeight = 60.dpToPx - } - addView(urlHolder) - - //collection of items in our menu and their click event - val data = arrayOf( - R.string.copy_link to { context.copyToClipboard(url) } - ) - - //add views and extract ids - val views = data.map { - (textId, onClick) -> - val tv = TextView(context).apply { - text = context.string(textId) - setOnClickListener({ onClick(); close() }) - } - addView(tv) - tv - }.toMutableList() - - views.add(0, urlHolder) - - val ids = views.mapIndexed { index, textView -> - textView.apply { - id = 74329 + index //totally arbitrary - setTextColor(tc) - setBackgroundColor(bg) - } - KL.d("ID ${textView.text}") - textView.id - } - - //clone to set only after ids are set - val set = ConstraintSet() - set.clone(this) - - ids.forEach { - set.connect(it, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, 16) - set.connect(it, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 16) - } - - - set.createVerticalChain(ConstraintSet.PARENT_ID, ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM, - ids.toIntArray(), null, ConstraintSet.CHAIN_PACKED) - - set.applyTo(this) - setOnClickListener { - close() - } - } - - fun close() { - transitionAuto() - gone() - } - - fun show(url: String) { - this.url = url - urlHolder.text = this.url - transitionAuto() - visible() - } - -} \ No newline at end of file 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 64bdf888..5583c63d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt @@ -28,7 +28,6 @@ class FrostWebView @JvmOverloads constructor( val refresh: SwipeRefreshLayout by bindView(R.id.swipe_refresh) val web: FrostWebViewCore by bindView(R.id.frost_webview_core) val progress: ProgressBar by bindView(R.id.progress_bar) - val contextMenu: FrostWebContextMenu by bindView(R.id.context_menu) init { inflate(getContext(), R.layout.swipe_webview, this) @@ -55,7 +54,7 @@ class FrostWebView @JvmOverloads constructor( @SuppressLint("SetJavaScriptEnabled") fun setupWebview(url: String, enum: FbTab? = null) { - with (web) { + with(web) { baseUrl = url baseEnum = enum with(settings) { @@ -63,12 +62,13 @@ class FrostWebView @JvmOverloads constructor( userAgentString = com.pitchedapps.frost.facebook.USER_AGENT_BASIC allowFileAccess = true defaultFontSize + textZoom = Prefs.webTextScaling } setLayerType(View.LAYER_TYPE_HARDWARE, null) frostWebClient = baseEnum?.webClient?.invoke(this) ?: FrostWebViewClient(this) webViewClient = frostWebClient webChromeClient = FrostChromeClient(this) - addJavascriptInterface(FrostJSI(context, this, contextMenu), "Frost") + addJavascriptInterface(FrostJSI(context, 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 4484dcdb..16a4a092 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt @@ -62,7 +62,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { webCore.jsInject(CssHider.HEADER, Prefs.themeInjector, JsAssets.CLICK_A.maybe(webCore.baseEnum != null), -// JsAssets.CONTEXT_A, + JsAssets.CONTEXT_A, callback = { refreshObservable.onNext(false) }) } 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 76b04f23..1e023dca 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt @@ -1,15 +1,12 @@ package com.pitchedapps.frost.web import android.animation.ValueAnimator -import android.annotation.SuppressLint import android.content.Context -import android.graphics.Color import android.support.v4.view.NestedScrollingChild import android.support.v4.view.NestedScrollingChildHelper import android.support.v4.view.ViewCompat import android.util.AttributeSet import android.view.MotionEvent -import android.view.View import android.view.animation.DecelerateInterpolator import android.webkit.WebView import ca.allanwang.kau.utils.circularReveal @@ -17,7 +14,6 @@ import ca.allanwang.kau.utils.fadeIn import ca.allanwang.kau.utils.fadeOut import ca.allanwang.kau.utils.isVisible import com.pitchedapps.frost.facebook.FbTab -import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.utils.Prefs import io.reactivex.Scheduler import io.reactivex.android.schedulers.AndroidSchedulers @@ -45,7 +41,7 @@ class FrostWebViewCore @JvmOverloads constructor( var baseUrl: String? = null var baseEnum: FbTab? = null //only viewpager items should pass the base enum - internal var frostWebClient: FrostWebViewClient? = null + internal lateinit var frostWebClient: FrostWebViewClient init { isNestedScrollingEnabled = true 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 fb2e1851..d4d08958 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewSearch.kt @@ -14,6 +14,7 @@ import com.pitchedapps.frost.injectors.JsAssets import com.pitchedapps.frost.injectors.JsBuilder import com.pitchedapps.frost.injectors.jsInject import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.Prefs import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject import org.jetbrains.anko.runOnUiThread @@ -62,10 +63,10 @@ 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")}") + L.v("Search element ${element.attr("href")}") val texts = element.select("div").map { (it.ownText()) }.filter { it.isNotBlank() } val pair = Pair(texts, element.attr("href")) - L.i("Search element potential $pair") + L.v("Search element potential $pair") pair }.filter { it.first.isNotEmpty() } } @@ -135,6 +136,7 @@ class FrostWebViewSearch(context: Context, val contract: SearchContract) : WebVi } 1 -> { //something is not found in the search view; this is effectively useless L.eThrow("Search subject error; reverting to full overlay") + Prefs.searchBar = false searchSubject.onComplete() contract.searchOverlayDispose() } -- cgit v1.2.3