mirror of
https://github.com/AllanWang/Frost-for-Facebook.git
synced 2024-11-08 12:02:33 +01:00
Enhancement/ktlint (#1259)
* Add spotless * Reformat code * Apply license header * Add remaining license headers
This commit is contained in:
parent
c45b30e28f
commit
697d01882b
@ -6,6 +6,8 @@ apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'com.getkeepsafe.dexcount'
|
||||
apply plugin: 'com.gladed.androidgitversion'
|
||||
|
||||
apply from: '../spotless.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion kau.targetSdk
|
||||
buildToolsVersion kau.buildTools
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost
|
||||
|
||||
import android.app.Activity
|
||||
@ -21,7 +37,11 @@ import com.pitchedapps.frost.facebook.FbCookie
|
||||
import com.pitchedapps.frost.glide.GlideApp
|
||||
import com.pitchedapps.frost.services.scheduleNotifications
|
||||
import com.pitchedapps.frost.services.setupNotificationChannels
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.BuildUtils
|
||||
import com.pitchedapps.frost.utils.FrostPglAdBlock
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.Showcase
|
||||
import com.raizlabs.android.dbflow.config.DatabaseConfig
|
||||
import com.raizlabs.android.dbflow.config.FlowConfig
|
||||
import com.raizlabs.android.dbflow.config.FlowManager
|
||||
@ -30,10 +50,9 @@ import io.reactivex.exceptions.UndeliverableException
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import java.net.SocketTimeoutException
|
||||
import java.net.UnknownHostException
|
||||
import java.util.*
|
||||
import java.util.Random
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-28.
|
||||
*/
|
||||
@ -46,10 +65,12 @@ class FrostApp : Application() {
|
||||
// lateinit var refWatcher: RefWatcher
|
||||
|
||||
private fun FlowConfig.Builder.withDatabase(name: String, klass: KClass<*>) =
|
||||
addDatabaseConfig(DatabaseConfig.builder(klass.java)
|
||||
.databaseName(name)
|
||||
.modelNotifier(ContentResolverNotifier("${BuildConfig.APPLICATION_ID}.dbflow.provider"))
|
||||
.build())
|
||||
addDatabaseConfig(
|
||||
DatabaseConfig.builder(klass.java)
|
||||
.databaseName(name)
|
||||
.modelNotifier(ContentResolverNotifier("${BuildConfig.APPLICATION_ID}.dbflow.provider"))
|
||||
.build()
|
||||
)
|
||||
|
||||
override fun onCreate() {
|
||||
if (!buildIsLollipopAndUp) { // not supported
|
||||
@ -57,11 +78,13 @@ class FrostApp : Application() {
|
||||
return
|
||||
}
|
||||
|
||||
FlowManager.init(FlowConfig.Builder(this)
|
||||
FlowManager.init(
|
||||
FlowConfig.Builder(this)
|
||||
.withDatabase(CookiesDb.NAME, CookiesDb::class)
|
||||
.withDatabase(FbTabsDb.NAME, FbTabsDb::class)
|
||||
.withDatabase(NotificationDb.NAME, NotificationDb::class)
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
Showcase.initialize(this, "${BuildConfig.APPLICATION_ID}.showcase")
|
||||
Prefs.initialize(this, "${BuildConfig.APPLICATION_ID}.prefs")
|
||||
// if (LeakCanary.isInAnalyzerProcess(this)) return
|
||||
@ -95,9 +118,11 @@ class FrostApp : Application() {
|
||||
val c = imageView.context
|
||||
val request = GlideApp.with(c)
|
||||
val old = request.load(uri).apply(RequestOptions().placeholder(placeholder))
|
||||
request.load(uri).apply(RequestOptions()
|
||||
.signature(ApplicationVersionSignature.obtain(c)))
|
||||
.thumbnail(old).into(imageView)
|
||||
request.load(uri).apply(
|
||||
RequestOptions()
|
||||
.signature(ApplicationVersionSignature.obtain(c))
|
||||
)
|
||||
.thumbnail(old).into(imageView)
|
||||
}
|
||||
})
|
||||
if (BuildConfig.DEBUG)
|
||||
@ -127,7 +152,6 @@ class FrostApp : Application() {
|
||||
L.e(it) { "RxJava error" }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initBugsnag() {
|
||||
@ -136,7 +160,7 @@ class FrostApp : Application() {
|
||||
Bugsnag.disableExceptionHandler()
|
||||
if (!BuildConfig.APPLICATION_ID.startsWith("com.pitchedapps.frost")) return
|
||||
val version = BuildUtils.match(BuildConfig.VERSION_NAME)
|
||||
?: return L.d { "Bugsnag disabled for ${BuildConfig.VERSION_NAME}" }
|
||||
?: return L.d { "Bugsnag disabled for ${BuildConfig.VERSION_NAME}" }
|
||||
Bugsnag.enableExceptionHandler()
|
||||
Bugsnag.setNotifyReleaseStages(*BuildUtils.getAllStages())
|
||||
Bugsnag.setAppVersion(version.versionName)
|
||||
@ -157,5 +181,4 @@ class FrostApp : Application() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost
|
||||
|
||||
import android.content.Intent
|
||||
@ -21,7 +37,8 @@ import com.pitchedapps.frost.utils.EXTRA_COOKIES
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.launchNewTask
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
import java.util.IllegalFormatException
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-28.
|
||||
@ -46,7 +63,8 @@ class StartActivity : KauBaseActivity() {
|
||||
if (Prefs.userId != -1L)
|
||||
startActivity<MainActivity>(intentBuilder = {
|
||||
putParcelableArrayListExtra(EXTRA_COOKIES, cookies)
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
||||
Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
})
|
||||
else
|
||||
launchNewTask<SelectorActivity>(cookies)
|
||||
@ -57,11 +75,10 @@ class StartActivity : KauBaseActivity() {
|
||||
} catch (e: Exception) {
|
||||
showInvalidWebView()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showInvalidWebView() =
|
||||
showInvalidView(R.string.error_webview)
|
||||
showInvalidView(R.string.error_webview)
|
||||
|
||||
private fun showInvalidSdkView() {
|
||||
val text = try {
|
||||
@ -73,12 +90,12 @@ class StartActivity : KauBaseActivity() {
|
||||
}
|
||||
|
||||
private fun showInvalidView(textRes: Int) =
|
||||
showInvalidView(string(textRes))
|
||||
showInvalidView(string(textRes))
|
||||
|
||||
private fun showInvalidView(text: String) {
|
||||
setContentView(R.layout.activity_invalid)
|
||||
findViewById<ImageView>(R.id.invalid_icon)
|
||||
.setIcon(GoogleMaterial.Icon.gmd_adb, -1, Color.WHITE)
|
||||
.setIcon(GoogleMaterial.Icon.gmd_adb, -1, Color.WHITE)
|
||||
findViewById<TextView>(R.id.invalid_text).text = text
|
||||
}
|
||||
}
|
@ -1,17 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ca.allanwang.kau.about.AboutActivityBase
|
||||
import ca.allanwang.kau.about.LibraryIItem
|
||||
import ca.allanwang.kau.adapters.FastItemThemedAdapter
|
||||
import ca.allanwang.kau.adapters.ThemableIItem
|
||||
import ca.allanwang.kau.adapters.ThemableIItemDelegate
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.dimenPixelSize
|
||||
import ca.allanwang.kau.utils.resolveDrawable
|
||||
import ca.allanwang.kau.utils.startLink
|
||||
import ca.allanwang.kau.utils.string
|
||||
import ca.allanwang.kau.utils.toDrawable
|
||||
import ca.allanwang.kau.utils.toast
|
||||
import ca.allanwang.kau.utils.withMinAlpha
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import com.mikepenz.aboutlibraries.entity.License
|
||||
@ -25,7 +48,6 @@ import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-26.
|
||||
*/
|
||||
@ -42,21 +64,21 @@ class AboutActivity : AboutActivityBase(null, {
|
||||
|
||||
override fun getLibraries(libs: Libs): List<Library> {
|
||||
val include = arrayOf(
|
||||
"AboutLibraries",
|
||||
"AndroidIconics",
|
||||
"androidin_appbillingv3",
|
||||
"androidslidinguppanel",
|
||||
"Crashlytics",
|
||||
"dbflow",
|
||||
"fastadapter",
|
||||
"glide",
|
||||
"Jsoup",
|
||||
"kau",
|
||||
"kotterknife",
|
||||
"materialdialogs",
|
||||
"materialdrawer",
|
||||
"rxjava",
|
||||
"subsamplingscaleimageview"
|
||||
"AboutLibraries",
|
||||
"AndroidIconics",
|
||||
"androidin_appbillingv3",
|
||||
"androidslidinguppanel",
|
||||
"Crashlytics",
|
||||
"dbflow",
|
||||
"fastadapter",
|
||||
"glide",
|
||||
"Jsoup",
|
||||
"kau",
|
||||
"kotterknife",
|
||||
"materialdialogs",
|
||||
"materialdrawer",
|
||||
"rxjava",
|
||||
"subsamplingscaleimageview"
|
||||
)
|
||||
|
||||
val l = libs.prepareLibraries(this, include, null, false, true, true)
|
||||
@ -136,11 +158,11 @@ class AboutActivity : AboutActivityBase(null, {
|
||||
val c = itemView.context
|
||||
val size = c.dimenPixelSize(R.dimen.kau_avatar_bounds)
|
||||
images = arrayOf<Pair<IIcon, () -> Unit>>(
|
||||
GoogleMaterial.Icon.gmd_arrow_downward to { c.startLink(R.string.github_downloads_url) },
|
||||
CommunityMaterial.Icon2.cmd_reddit to { c.startLink(R.string.reddit_url) },
|
||||
CommunityMaterial.Icon.cmd_github_circle to { c.startLink(R.string.github_url) },
|
||||
CommunityMaterial.Icon2.cmd_slack to { c.startLink(R.string.slack_url) },
|
||||
CommunityMaterial.Icon2.cmd_xda to { c.startLink(R.string.xda_url) }
|
||||
GoogleMaterial.Icon.gmd_arrow_downward to { c.startLink(R.string.github_downloads_url) },
|
||||
CommunityMaterial.Icon2.cmd_reddit to { c.startLink(R.string.reddit_url) },
|
||||
CommunityMaterial.Icon.cmd_github_circle to { c.startLink(R.string.github_url) },
|
||||
CommunityMaterial.Icon2.cmd_slack to { c.startLink(R.string.slack_url) },
|
||||
CommunityMaterial.Icon2.cmd_xda to { c.startLink(R.string.xda_url) }
|
||||
).mapIndexed { i, (icon, onClick) ->
|
||||
ImageView(c).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(size, size)
|
||||
@ -154,8 +176,14 @@ class AboutActivity : AboutActivityBase(null, {
|
||||
}
|
||||
val set = ConstraintSet()
|
||||
set.clone(container)
|
||||
set.createHorizontalChain(ConstraintSet.PARENT_ID, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT,
|
||||
images.map { it.id }.toIntArray(), null, ConstraintSet.CHAIN_SPREAD_INSIDE)
|
||||
set.createHorizontalChain(ConstraintSet.PARENT_ID,
|
||||
ConstraintSet.LEFT,
|
||||
ConstraintSet.PARENT_ID,
|
||||
ConstraintSet.RIGHT,
|
||||
images.map { it.id }.toIntArray(),
|
||||
null,
|
||||
ConstraintSet.CHAIN_SPREAD_INSIDE
|
||||
)
|
||||
set.applyTo(container)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.content.res.Configuration
|
||||
@ -80,7 +96,6 @@ abstract class BaseActivity : KauBaseActivity() {
|
||||
//// disposeNetworkConnectivity()
|
||||
// }
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
if (this is VideoViewHolder) videoOnStop()
|
||||
super.onStop()
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -8,25 +24,31 @@ import android.graphics.PointF
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.webkit.ValueCallback
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebView
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import ca.allanwang.kau.searchview.SearchItem
|
||||
import ca.allanwang.kau.searchview.SearchView
|
||||
import ca.allanwang.kau.searchview.SearchViewHolder
|
||||
import ca.allanwang.kau.searchview.bindSearchView
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.fadeScaleTransition
|
||||
import ca.allanwang.kau.utils.restart
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.setMenuIcons
|
||||
import ca.allanwang.kau.utils.showIf
|
||||
import ca.allanwang.kau.utils.string
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.toast
|
||||
import ca.allanwang.kau.utils.withMinAlpha
|
||||
import co.zsmb.materialdrawerkt.builders.Builder
|
||||
import co.zsmb.materialdrawerkt.builders.accountHeader
|
||||
import co.zsmb.materialdrawerkt.builders.drawer
|
||||
@ -35,6 +57,9 @@ import co.zsmb.materialdrawerkt.draweritems.badgeable.secondaryItem
|
||||
import co.zsmb.materialdrawerkt.draweritems.divider
|
||||
import co.zsmb.materialdrawerkt.draweritems.profile.profile
|
||||
import co.zsmb.materialdrawerkt.draweritems.profile.profileSetting
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
@ -57,7 +82,26 @@ import com.pitchedapps.frost.facebook.parsers.SearchParser
|
||||
import com.pitchedapps.frost.facebook.profilePictureUrl
|
||||
import com.pitchedapps.frost.fragments.BaseFragment
|
||||
import com.pitchedapps.frost.fragments.WebFragment
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.ACTIVITY_SETTINGS
|
||||
import com.pitchedapps.frost.utils.EXTRA_COOKIES
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.MAIN_TIMEOUT_DURATION
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.REQUEST_NAV
|
||||
import com.pitchedapps.frost.utils.REQUEST_REFRESH
|
||||
import com.pitchedapps.frost.utils.REQUEST_RESTART
|
||||
import com.pitchedapps.frost.utils.REQUEST_RESTART_APPLICATION
|
||||
import com.pitchedapps.frost.utils.REQUEST_SEARCH
|
||||
import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM
|
||||
import com.pitchedapps.frost.utils.cookies
|
||||
import com.pitchedapps.frost.utils.frostChangelog
|
||||
import com.pitchedapps.frost.utils.frostEvent
|
||||
import com.pitchedapps.frost.utils.frostNavigationBar
|
||||
import com.pitchedapps.frost.utils.launchLogin
|
||||
import com.pitchedapps.frost.utils.launchNewTask
|
||||
import com.pitchedapps.frost.utils.launchWebOverlay
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.utils.setFrostColors
|
||||
import com.pitchedapps.frost.views.BadgedIcon
|
||||
import com.pitchedapps.frost.views.FrostVideoViewer
|
||||
import com.pitchedapps.frost.views.FrostViewPager
|
||||
@ -68,8 +112,8 @@ import com.pitchedapps.frost.views.FrostViewPager
|
||||
* Most of the logic that is unrelated to handling fragments
|
||||
*/
|
||||
abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
FileChooserContract by FileChooserDelegate(),
|
||||
VideoViewHolder, SearchViewHolder {
|
||||
FileChooserContract by FileChooserDelegate(),
|
||||
VideoViewHolder, SearchViewHolder {
|
||||
|
||||
protected lateinit var adapter: SectionsPagerAdapter
|
||||
override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper)
|
||||
@ -111,12 +155,14 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
Prefs.versionCode = BuildConfig.VERSION_CODE
|
||||
if (!BuildConfig.DEBUG) {
|
||||
frostChangelog()
|
||||
frostEvent("Version",
|
||||
"Version code" to BuildConfig.VERSION_CODE,
|
||||
"Prev version code" to Prefs.prevVersionCode,
|
||||
"Version name" to BuildConfig.VERSION_NAME,
|
||||
"Build type" to BuildConfig.BUILD_TYPE,
|
||||
"Frost id" to Prefs.frostId)
|
||||
frostEvent(
|
||||
"Version",
|
||||
"Version code" to BuildConfig.VERSION_CODE,
|
||||
"Prev version code" to Prefs.prevVersionCode,
|
||||
"Version name" to BuildConfig.VERSION_NAME,
|
||||
"Build type" to BuildConfig.BUILD_TYPE,
|
||||
"Frost id" to Prefs.frostId
|
||||
)
|
||||
}
|
||||
}
|
||||
setupDrawer(savedInstanceState)
|
||||
@ -204,7 +250,9 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
identifier = -2L
|
||||
}
|
||||
profileSetting(nameRes = R.string.kau_add_account) {
|
||||
iconDrawable = IconicsDrawable(this@BaseMainActivity, GoogleMaterial.Icon.gmd_add).actionBar().paddingDp(5).color(Prefs.textColor)
|
||||
iconDrawable =
|
||||
IconicsDrawable(this@BaseMainActivity, GoogleMaterial.Icon.gmd_add).actionBar().paddingDp(5)
|
||||
.color(Prefs.textColor)
|
||||
textColor = Prefs.textColor.toLong()
|
||||
identifier = -3L
|
||||
}
|
||||
@ -225,8 +273,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
} else {
|
||||
materialDialogThemed {
|
||||
title(R.string.kau_logout)
|
||||
content(String.format(string(R.string.kau_logout_confirm_as_x), currentCookie.name
|
||||
?: Prefs.userId.toString()))
|
||||
content(
|
||||
String.format(
|
||||
string(R.string.kau_logout_confirm_as_x), currentCookie.name
|
||||
?: Prefs.userId.toString()
|
||||
)
|
||||
)
|
||||
positiveText(R.string.kau_yes)
|
||||
negativeText(R.string.kau_no)
|
||||
onPositive { _, _ -> FbCookie.logout(this@BaseMainActivity) }
|
||||
@ -295,9 +347,11 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
toolbar.tint(Prefs.iconColor)
|
||||
setMenuIcons(menu, Prefs.iconColor,
|
||||
R.id.action_settings to GoogleMaterial.Icon.gmd_settings,
|
||||
R.id.action_search to GoogleMaterial.Icon.gmd_search)
|
||||
setMenuIcons(
|
||||
menu, Prefs.iconColor,
|
||||
R.id.action_settings to GoogleMaterial.Icon.gmd_settings,
|
||||
R.id.action_search to GoogleMaterial.Icon.gmd_search
|
||||
)
|
||||
searchViewBindIfNull {
|
||||
bindSearchView(menu, R.id.action_search, Prefs.iconColor) {
|
||||
textCallback = { query, searchView ->
|
||||
@ -309,7 +363,13 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
if (data != null) {
|
||||
val items = data.mapTo(mutableListOf(), FrostSearch::toSearchItem)
|
||||
if (items.isNotEmpty())
|
||||
items.add(SearchItem("${FbItem._SEARCH.url}?q=$query", string(R.string.show_all_results), iicon = null))
|
||||
items.add(
|
||||
SearchItem(
|
||||
"${FbItem._SEARCH.url}?q=$query",
|
||||
string(R.string.show_all_results),
|
||||
iicon = null
|
||||
)
|
||||
)
|
||||
searchViewCache[query] = items
|
||||
searchView.results = items
|
||||
}
|
||||
@ -332,7 +392,8 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
R.id.action_settings -> {
|
||||
val intent = Intent(this, SettingsActivity::class.java)
|
||||
intent.putParcelableArrayListExtra(EXTRA_COOKIES, cookies())
|
||||
val bundle = ActivityOptions.makeCustomAnimation(this, R.anim.kau_slide_in_right, R.anim.kau_fade_out).toBundle()
|
||||
val bundle =
|
||||
ActivityOptions.makeCustomAnimation(this, R.anim.kau_slide_in_right, R.anim.kau_fade_out).toBundle()
|
||||
startActivityForResult(intent, ACTIVITY_SETTINGS, bundle)
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
@ -340,7 +401,10 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
return true
|
||||
}
|
||||
|
||||
override fun openFileChooser(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams) {
|
||||
override fun openFileChooser(
|
||||
filePathCallback: ValueCallback<Array<Uri>?>,
|
||||
fileChooserParams: WebChromeClient.FileChooserParams
|
||||
) {
|
||||
openMediaPicker(filePathCallback, fileChooserParams)
|
||||
}
|
||||
|
||||
@ -377,8 +441,10 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
adapter.forcedFallbacks.clear()
|
||||
adapter.forcedFallbacks.addAll(savedInstanceState.getStringArrayList(STATE_FORCE_FALLBACK)
|
||||
?: emptyList())
|
||||
adapter.forcedFallbacks.addAll(
|
||||
savedInstanceState.getStringArrayList(STATE_FORCE_FALLBACK)
|
||||
?: emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -444,10 +510,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
val item = pages[position]
|
||||
return BaseFragment(item.fragmentCreator,
|
||||
forcedFallbacks.contains(item.name),
|
||||
item,
|
||||
position)
|
||||
return BaseFragment(
|
||||
item.fragmentCreator,
|
||||
forcedFallbacks.contains(item.name),
|
||||
item,
|
||||
position
|
||||
)
|
||||
}
|
||||
|
||||
override fun getCount() = pages.size
|
||||
@ -455,12 +523,12 @@ abstract class BaseMainActivity : BaseActivity(), MainActivityContract,
|
||||
override fun getPageTitle(position: Int): CharSequence = getString(pages[position].titleId)
|
||||
|
||||
override fun getItemPosition(fragment: Any) =
|
||||
if (fragment !is BaseFragment)
|
||||
POSITION_UNCHANGED
|
||||
else if (fragment is WebFragment || fragment.valid)
|
||||
POSITION_UNCHANGED
|
||||
else
|
||||
POSITION_NONE
|
||||
if (fragment !is BaseFragment)
|
||||
POSITION_UNCHANGED
|
||||
else if (fragment is WebFragment || fragment.valid)
|
||||
POSITION_UNCHANGED
|
||||
else
|
||||
POSITION_NONE
|
||||
}
|
||||
|
||||
override val lowerVideoPadding: PointF
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.app.Activity
|
||||
@ -9,9 +25,9 @@ import ca.allanwang.kau.internal.KauBaseActivity
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
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.R
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.createFreshDir
|
||||
@ -73,23 +89,22 @@ class DebugActivity : KauBaseActivity() {
|
||||
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, debug_webview.url)
|
||||
intent.putExtra(RESULT_SCREENSHOT, screenshot)
|
||||
if (body != null)
|
||||
intent.putExtra(RESULT_BODY, body)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
.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, debug_webview.url)
|
||||
intent.putExtra(RESULT_SCREENSHOT, screenshot)
|
||||
if (body != null)
|
||||
intent.putExtra(RESULT_BODY, body)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.content.Intent
|
||||
@ -5,16 +21,25 @@ import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import android.view.View
|
||||
import ca.allanwang.kau.internal.KauBaseActivity
|
||||
import ca.allanwang.kau.logging.KauLoggerExtension
|
||||
import ca.allanwang.kau.mediapicker.scanMedia
|
||||
import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
|
||||
import ca.allanwang.kau.permissions.kauRequestPermissions
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.colorToForeground
|
||||
import ca.allanwang.kau.utils.fadeOut
|
||||
import ca.allanwang.kau.utils.fadeScaleTransition
|
||||
import ca.allanwang.kau.utils.isHidden
|
||||
import ca.allanwang.kau.utils.scaleXY
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.use
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import ca.allanwang.kau.utils.withMinAlpha
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.pitchedapps.frost.R
|
||||
@ -23,7 +48,19 @@ import com.pitchedapps.frost.facebook.get
|
||||
import com.pitchedapps.frost.facebook.requests.call
|
||||
import com.pitchedapps.frost.facebook.requests.getFullSizedImageUrl
|
||||
import com.pitchedapps.frost.facebook.requests.requestBuilder
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.ARG_COOKIE
|
||||
import com.pitchedapps.frost.utils.ARG_IMAGE_URL
|
||||
import com.pitchedapps.frost.utils.ARG_TEXT
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.createFreshFile
|
||||
import com.pitchedapps.frost.utils.frostSnackbar
|
||||
import com.pitchedapps.frost.utils.frostUriFromFile
|
||||
import com.pitchedapps.frost.utils.isIndirectImageUrl
|
||||
import com.pitchedapps.frost.utils.logFrostEvent
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.utils.sendFrostEmail
|
||||
import com.pitchedapps.frost.utils.setFrostColors
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout
|
||||
import kotlinx.android.synthetic.main.activity_image.*
|
||||
import okhttp3.Response
|
||||
@ -34,7 +71,8 @@ import java.io.File
|
||||
import java.io.FileFilter
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-07-15.
|
||||
@ -94,8 +132,10 @@ class ImageActivity : KauBaseActivity() {
|
||||
|
||||
// a unique image identifier based on the id (if it exists), and its hash
|
||||
private val imageHash: String by lazy {
|
||||
"${Math.abs(FB_IMAGE_ID_MATCHER.find(imageUrl)[1]?.hashCode()
|
||||
?: 0)}_${Math.abs(imageUrl.hashCode())}"
|
||||
"${Math.abs(
|
||||
FB_IMAGE_ID_MATCHER.find(imageUrl)[1]?.hashCode()
|
||||
?: 0
|
||||
)}_${Math.abs(imageUrl.hashCode())}"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -105,11 +145,15 @@ class ImageActivity : KauBaseActivity() {
|
||||
L.v { "Displaying image $imageUrl" }
|
||||
val layout = if (!imageText.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless
|
||||
setContentView(layout)
|
||||
image_container.setBackgroundColor(if (Prefs.blackMediaBg) Color.BLACK
|
||||
else Prefs.bgColor.withMinAlpha(222))
|
||||
image_container.setBackgroundColor(
|
||||
if (Prefs.blackMediaBg) Color.BLACK
|
||||
else Prefs.bgColor.withMinAlpha(222)
|
||||
)
|
||||
image_text?.setTextColor(if (Prefs.blackMediaBg) Color.WHITE else Prefs.textColor)
|
||||
image_text?.setBackgroundColor((if (Prefs.blackMediaBg) Color.BLACK else Prefs.bgColor)
|
||||
.colorToForeground(0.2f).withAlpha(255))
|
||||
image_text?.setBackgroundColor(
|
||||
(if (Prefs.blackMediaBg) Color.BLACK else Prefs.bgColor)
|
||||
.colorToForeground(0.2f).withAlpha(255)
|
||||
)
|
||||
image_text?.text = imageText
|
||||
image_progress.tint(if (Prefs.blackMediaBg) Color.WHITE else Prefs.accentColor)
|
||||
image_panel?.addPanelSlideListener(object : SlidingUpPanelLayout.SimplePanelSlideListener() {
|
||||
@ -208,16 +252,15 @@ class ImageActivity : KauBaseActivity() {
|
||||
}
|
||||
|
||||
private fun getImageResponse(): Response = cookie.requestBuilder()
|
||||
.url(trueImageUrl)
|
||||
.get()
|
||||
.call()
|
||||
.execute()
|
||||
|
||||
.url(trueImageUrl)
|
||||
.get()
|
||||
.call()
|
||||
.execute()
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun downloadImageTo(file: File) {
|
||||
val body = getImageResponse().body()
|
||||
?: throw IOException("Failed to retrieve image body")
|
||||
?: throw IOException("Failed to retrieve image body")
|
||||
body.byteStream().use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
@ -272,7 +315,11 @@ class ImageActivity : KauBaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class FabStates(val iicon: IIcon, val iconColor: Int = Prefs.iconColor, val backgroundTint: Int = Int.MAX_VALUE) {
|
||||
internal enum class FabStates(
|
||||
val iicon: IIcon,
|
||||
val iconColor: Int = Prefs.iconColor,
|
||||
val backgroundTint: Int = Int.MAX_VALUE
|
||||
) {
|
||||
ERROR(GoogleMaterial.Icon.gmd_error, Color.WHITE, Color.RED) {
|
||||
override fun onClick(activity: ImageActivity) {
|
||||
activity.materialDialogThemed {
|
||||
@ -334,5 +381,4 @@ internal enum class FabStates(val iicon: IIcon, val iconColor: Int = Prefs.iconC
|
||||
}
|
||||
|
||||
abstract fun onClick(activity: ImageActivity)
|
||||
|
||||
}
|
@ -1,31 +1,60 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import ca.allanwang.kau.internal.KauBaseActivity
|
||||
import ca.allanwang.kau.ui.views.RippleCanvas
|
||||
import ca.allanwang.kau.ui.widgets.InkPageIndicator
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.blendWith
|
||||
import ca.allanwang.kau.utils.color
|
||||
import ca.allanwang.kau.utils.fadeScaleTransition
|
||||
import ca.allanwang.kau.utils.navigationBarColor
|
||||
import ca.allanwang.kau.utils.postDelayed
|
||||
import ca.allanwang.kau.utils.scaleXY
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.statusBarColor
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.intro.*
|
||||
import com.pitchedapps.frost.intro.BaseIntroFragment
|
||||
import com.pitchedapps.frost.intro.IntroAccountFragment
|
||||
import com.pitchedapps.frost.intro.IntroFragmentEnd
|
||||
import com.pitchedapps.frost.intro.IntroFragmentTheme
|
||||
import com.pitchedapps.frost.intro.IntroFragmentWelcome
|
||||
import com.pitchedapps.frost.intro.IntroTabContextFragment
|
||||
import com.pitchedapps.frost.intro.IntroTabTouchFragment
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.cookies
|
||||
import com.pitchedapps.frost.utils.launchNewTask
|
||||
import org.jetbrains.anko.find
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-07-25.
|
||||
*
|
||||
@ -43,12 +72,12 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
|
||||
private var barHasNext = true
|
||||
|
||||
val fragments = listOf(
|
||||
IntroFragmentWelcome(),
|
||||
IntroFragmentTheme(),
|
||||
IntroAccountFragment(),
|
||||
IntroTabTouchFragment(),
|
||||
IntroTabContextFragment(),
|
||||
IntroFragmentEnd()
|
||||
IntroFragmentWelcome(),
|
||||
IntroFragmentTheme(),
|
||||
IntroAccountFragment(),
|
||||
IntroTabTouchFragment(),
|
||||
IntroTabContextFragment(),
|
||||
IntroFragmentEnd()
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -97,7 +126,6 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
|
||||
page.alpha = 1f
|
||||
page.translationX = 0f
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun finish(x: Float, y: Float) {
|
||||
@ -107,9 +135,11 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
|
||||
postDelayed(1000) { finish() }
|
||||
}
|
||||
val lastView: View? = fragments.last().view
|
||||
arrayOf<View?>(skip, indicator, next,
|
||||
lastView?.find(R.id.intro_title),
|
||||
lastView?.find(R.id.intro_desc)).forEach {
|
||||
arrayOf<View?>(
|
||||
skip, indicator, next,
|
||||
lastView?.find(R.id.intro_title),
|
||||
lastView?.find(R.id.intro_desc)
|
||||
).forEach {
|
||||
it?.animate()?.alpha(0f)?.setDuration(600)?.start()
|
||||
}
|
||||
if (Prefs.textColor != Color.WHITE) {
|
||||
@ -147,7 +177,6 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
@ -162,16 +191,19 @@ class IntroActivity : KauBaseActivity(), ViewPager.PageTransformer, ViewPager.On
|
||||
if (barHasNext == hasNext) return
|
||||
barHasNext = hasNext
|
||||
next.fadeScaleTransition {
|
||||
setIcon(if (barHasNext) GoogleMaterial.Icon.gmd_navigate_next else GoogleMaterial.Icon.gmd_done, color = Prefs.textColor)
|
||||
setIcon(
|
||||
if (barHasNext) GoogleMaterial.Icon.gmd_navigate_next else GoogleMaterial.Icon.gmd_done,
|
||||
color = Prefs.textColor
|
||||
)
|
||||
}
|
||||
skip.animate().scaleXY(if (barHasNext) 1f else 0f)
|
||||
}
|
||||
|
||||
class IntroPageAdapter(fm: FragmentManager, private val fragments: List<BaseIntroFragment>) : FragmentPagerAdapter(fm) {
|
||||
class IntroPageAdapter(fm: FragmentManager, private val fragments: List<BaseIntroFragment>) :
|
||||
FragmentPagerAdapter(fm) {
|
||||
|
||||
override fun getItem(position: Int): Fragment = fragments[position]
|
||||
|
||||
override fun getCount(): Int = fragments.size
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,28 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import android.widget.ImageView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.fadeIn
|
||||
import ca.allanwang.kau.utils.fadeOut
|
||||
@ -24,14 +40,18 @@ import com.pitchedapps.frost.facebook.profilePictureUrl
|
||||
import com.pitchedapps.frost.glide.FrostGlide
|
||||
import com.pitchedapps.frost.glide.GlideApp
|
||||
import com.pitchedapps.frost.glide.transform
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Showcase
|
||||
import com.pitchedapps.frost.utils.frostEvent
|
||||
import com.pitchedapps.frost.utils.launchNewTask
|
||||
import com.pitchedapps.frost.utils.logFrostEvent
|
||||
import com.pitchedapps.frost.utils.setFrostColors
|
||||
import com.pitchedapps.frost.web.LoginWebView
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.subjects.SingleSubject
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-01.
|
||||
*/
|
||||
@ -78,51 +98,62 @@ class LoginActivity : BaseActivity() {
|
||||
private fun loadInfo(cookie: CookieModel) {
|
||||
refresh = true
|
||||
Single.zip<Boolean, String, Pair<Boolean, String>>(
|
||||
profileSubject,
|
||||
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()
|
||||
frostEvent("Login", "success" to 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<IntroActivity>(cookies, true)
|
||||
else
|
||||
launchNewTask<MainActivity>(cookies, true)
|
||||
}, 1000)
|
||||
}
|
||||
}.disposeOnDestroy()
|
||||
profileSubject,
|
||||
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()
|
||||
frostEvent("Login", "success" to 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<IntroActivity>(cookies, true)
|
||||
else
|
||||
launchNewTask<MainActivity>(cookies, true)
|
||||
}, 1000)
|
||||
}
|
||||
}.disposeOnDestroy()
|
||||
loadProfile(cookie.id)
|
||||
loadUsername(cookie)
|
||||
}
|
||||
|
||||
|
||||
private fun loadProfile(id: Long) {
|
||||
profileLoader.load(profilePictureUrl(id))
|
||||
.transform(FrostGlide.roundCorner).listener(object : RequestListener<Drawable> {
|
||||
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
profileSubject.onSuccess(true)
|
||||
return false
|
||||
}
|
||||
.transform(FrostGlide.roundCorner).listener(object : RequestListener<Drawable> {
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
profileSubject.onSuccess(true)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||
e.logFrostEvent("Profile loading exception")
|
||||
profileSubject.onSuccess(false)
|
||||
return false
|
||||
}
|
||||
}).into(profile)
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
e.logFrostEvent("Profile loading exception")
|
||||
profileSubject.onSuccess(false)
|
||||
return false
|
||||
}
|
||||
}).into(profile)
|
||||
}
|
||||
|
||||
private fun loadUsername(cookie: CookieModel) {
|
||||
@ -146,5 +177,4 @@ class LoginActivity : BaseActivity() {
|
||||
web.pauseTimers()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,24 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.views.BadgedIcon
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
@ -36,19 +52,19 @@ class MainActivity : BaseMainActivity() {
|
||||
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||
val delta = positionOffset * (255 - 128).toFloat()
|
||||
tabsForEachView { tabPosition, view ->
|
||||
view.setAllAlpha(when (tabPosition) {
|
||||
position -> 255.0f - delta
|
||||
position + 1 -> 128.0f + delta
|
||||
else -> 128f
|
||||
})
|
||||
view.setAllAlpha(
|
||||
when (tabPosition) {
|
||||
position -> 255.0f - delta
|
||||
position + 1 -> 128.0f + delta
|
||||
else -> 128f
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
viewPager.post { fragmentSubject.onNext(0); lastPosition = 0 } //trigger hook so title is set
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun setupTabs() {
|
||||
viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
|
||||
tabs.addOnTabSelectedListener(object : TabLayout.ViewPagerOnTabSelectedListener(viewPager) {
|
||||
@ -63,31 +79,31 @@ class MainActivity : BaseMainActivity() {
|
||||
}
|
||||
})
|
||||
headerBadgeObservable.throttleFirst(15, TimeUnit.SECONDS)
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.map { Jsoup.parse(it) }
|
||||
.filter { it.select("[data-sigil=count]").size >= 0 } //ensure headers exist
|
||||
.map {
|
||||
val feed = it.select("[data-sigil*=feed] [data-sigil=count]")
|
||||
val requests = it.select("[data-sigil*=requests] [data-sigil=count]")
|
||||
val messages = it.select("[data-sigil*=messages] [data-sigil=count]")
|
||||
val notifications = it.select("[data-sigil*=notifications] [data-sigil=count]")
|
||||
return@map arrayOf(feed, requests, messages, notifications).map { e -> e?.getOrNull(0)?.ownText() }
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { (feed, requests, messages, notifications) ->
|
||||
tabsForEachView { _, view ->
|
||||
when (view.iicon) {
|
||||
FbItem.FEED.icon -> view.badgeText = feed
|
||||
FbItem.FRIENDS.icon -> view.badgeText = requests
|
||||
FbItem.MESSAGES.icon -> view.badgeText = messages
|
||||
FbItem.NOTIFICATIONS.icon -> view.badgeText = notifications
|
||||
}
|
||||
.subscribeOn(Schedulers.newThread())
|
||||
.map { Jsoup.parse(it) }
|
||||
.filter { it.select("[data-sigil=count]").size >= 0 } //ensure headers exist
|
||||
.map {
|
||||
val feed = it.select("[data-sigil*=feed] [data-sigil=count]")
|
||||
val requests = it.select("[data-sigil*=requests] [data-sigil=count]")
|
||||
val messages = it.select("[data-sigil*=messages] [data-sigil=count]")
|
||||
val notifications = it.select("[data-sigil*=notifications] [data-sigil=count]")
|
||||
return@map arrayOf(feed, requests, messages, notifications).map { e -> e?.getOrNull(0)?.ownText() }
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { (feed, requests, messages, notifications) ->
|
||||
tabsForEachView { _, view ->
|
||||
when (view.iicon) {
|
||||
FbItem.FEED.icon -> view.badgeText = feed
|
||||
FbItem.FRIENDS.icon -> view.badgeText = requests
|
||||
FbItem.MESSAGES.icon -> view.badgeText = messages
|
||||
FbItem.NOTIFICATIONS.icon -> view.badgeText = notifications
|
||||
}
|
||||
}.disposeOnDestroy()
|
||||
}
|
||||
}.disposeOnDestroy()
|
||||
adapter.pages.forEach {
|
||||
tabs.addTab(tabs.newTab()
|
||||
.setCustomView(BadgedIcon(this).apply { iicon = it.icon }))
|
||||
.setCustomView(BadgedIcon(this).apply { iicon = it.icon })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,27 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.View
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -12,14 +28,33 @@ import ca.allanwang.kau.kpref.activity.CoreAttributeContract
|
||||
import ca.allanwang.kau.kpref.activity.KPrefActivity
|
||||
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
|
||||
import ca.allanwang.kau.ui.views.RippleCanvas
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.finishSlideOut
|
||||
import ca.allanwang.kau.utils.setMenuIcons
|
||||
import ca.allanwang.kau.utils.startActivityForResult
|
||||
import ca.allanwang.kau.utils.startLink
|
||||
import ca.allanwang.kau.utils.string
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.withSceneTransitionAnimation
|
||||
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.enums.Support
|
||||
import com.pitchedapps.frost.settings.*
|
||||
import com.pitchedapps.frost.utils.*
|
||||
|
||||
import com.pitchedapps.frost.settings.getAppearancePrefs
|
||||
import com.pitchedapps.frost.settings.getBehaviourPrefs
|
||||
import com.pitchedapps.frost.settings.getDebugPrefs
|
||||
import com.pitchedapps.frost.settings.getExperimentalPrefs
|
||||
import com.pitchedapps.frost.settings.getFeedPrefs
|
||||
import com.pitchedapps.frost.settings.getNotificationPrefs
|
||||
import com.pitchedapps.frost.settings.sendDebug
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.REQUEST_RESTART
|
||||
import com.pitchedapps.frost.utils.cookies
|
||||
import com.pitchedapps.frost.utils.frostChangelog
|
||||
import com.pitchedapps.frost.utils.frostNavigationBar
|
||||
import com.pitchedapps.frost.utils.launchNewTask
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.utils.setFrostTheme
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-06.
|
||||
@ -146,7 +181,6 @@ class SettingsActivity : KPrefActivity() {
|
||||
iicon = CommunityMaterial.Icon.cmd_android_debug_bridge
|
||||
visible = { Prefs.debugSettings }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun shouldRestartMain() {
|
||||
@ -179,9 +213,11 @@ class SettingsActivity : KPrefActivity() {
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_settings, menu)
|
||||
toolbar.tint(Prefs.iconColor)
|
||||
setMenuIcons(menu, Prefs.iconColor,
|
||||
R.id.action_email to GoogleMaterial.Icon.gmd_email,
|
||||
R.id.action_changelog to GoogleMaterial.Icon.gmd_info)
|
||||
setMenuIcons(
|
||||
menu, Prefs.iconColor,
|
||||
R.id.action_email to GoogleMaterial.Icon.gmd_email,
|
||||
R.id.action_changelog to GoogleMaterial.Icon.gmd_info
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.app.Activity
|
||||
@ -5,21 +21,18 @@ import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ca.allanwang.kau.kotlin.lazyContext
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.scaleXY
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import com.pitchedapps.frost.R
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
|
||||
import com.mikepenz.fastadapter_extensions.drag.ItemTouchCallback
|
||||
import com.mikepenz.fastadapter_extensions.drag.SimpleDragCallback
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.dbflow.TAB_COUNT
|
||||
import com.pitchedapps.frost.dbflow.loadFbTabs
|
||||
import com.pitchedapps.frost.dbflow.save
|
||||
@ -28,7 +41,7 @@ import com.pitchedapps.frost.iitems.TabIItem
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.setFrostColors
|
||||
import kotlinx.android.synthetic.main.activity_tab_customizer.*
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 26/11/17.
|
||||
@ -96,9 +109,9 @@ class TabCustomizerActivity : BaseActivity() {
|
||||
override fun itemTouchDropped(oldPosition: Int, newPosition: Int) = Unit
|
||||
}
|
||||
|
||||
|
||||
private class TabDragCallback(
|
||||
directions: Int, itemTouchCallback: ItemTouchCallback
|
||||
directions: Int,
|
||||
itemTouchCallback: ItemTouchCallback
|
||||
) : SimpleDragCallback(directions, itemTouchCallback) {
|
||||
|
||||
private var draggingView: TabIItem.ViewHolder? = null
|
||||
@ -122,7 +135,5 @@ class TabCustomizerActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -5,25 +21,52 @@ import android.content.Intent
|
||||
import android.graphics.PointF
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.webkit.ValueCallback
|
||||
import android.webkit.WebChromeClient
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import ca.allanwang.kau.swipe.kauSwipeOnCreate
|
||||
import ca.allanwang.kau.swipe.kauSwipeOnDestroy
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.copyToClipboard
|
||||
import ca.allanwang.kau.utils.darken
|
||||
import ca.allanwang.kau.utils.dpToPx
|
||||
import ca.allanwang.kau.utils.finishSlideOut
|
||||
import ca.allanwang.kau.utils.navigationBarColor
|
||||
import ca.allanwang.kau.utils.setMenuIcons
|
||||
import ca.allanwang.kau.utils.shareText
|
||||
import ca.allanwang.kau.utils.statusBarColor
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.toDrawable
|
||||
import ca.allanwang.kau.utils.toast
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
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.*
|
||||
import com.pitchedapps.frost.contracts.ActivityContract
|
||||
import com.pitchedapps.frost.contracts.FileChooserContract
|
||||
import com.pitchedapps.frost.contracts.FileChooserDelegate
|
||||
import com.pitchedapps.frost.contracts.FrostContentContainer
|
||||
import com.pitchedapps.frost.contracts.VideoViewHolder
|
||||
import com.pitchedapps.frost.enums.OverlayContext
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
import com.pitchedapps.frost.facebook.FbCookie
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
import com.pitchedapps.frost.facebook.formattedFbUrl
|
||||
import com.pitchedapps.frost.services.FrostRunnable
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.ARG_URL
|
||||
import com.pitchedapps.frost.utils.ARG_USER_ID
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.Showcase
|
||||
import com.pitchedapps.frost.utils.frostSnackbar
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.utils.setFrostColors
|
||||
import com.pitchedapps.frost.views.FrostContentWeb
|
||||
import com.pitchedapps.frost.views.FrostVideoViewer
|
||||
import com.pitchedapps.frost.views.FrostWebView
|
||||
@ -31,7 +74,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-01.
|
||||
*
|
||||
@ -103,8 +145,8 @@ class WebOverlayActivity : WebOverlayActivityBase(false)
|
||||
|
||||
@SuppressLint("Registered")
|
||||
open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseActivity(),
|
||||
ActivityContract, FrostContentContainer,
|
||||
VideoViewHolder, FileChooserContract by FileChooserDelegate() {
|
||||
ActivityContract, FrostContentContainer,
|
||||
VideoViewHolder, FileChooserContract by FileChooserDelegate() {
|
||||
|
||||
override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper)
|
||||
val toolbar: Toolbar by bindView(R.id.overlay_toolbar)
|
||||
@ -156,9 +198,9 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
|
||||
content.bind(this)
|
||||
|
||||
content.titleObservable
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { toolbar.title = it }
|
||||
.disposeOnDestroy()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { toolbar.title = it }
|
||||
.disposeOnDestroy()
|
||||
|
||||
with(web) {
|
||||
if (forceBasicAgent) //todo check; the webview already adds it dynamically
|
||||
@ -235,7 +277,10 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
|
||||
kauSwipeOnDestroy()
|
||||
}
|
||||
|
||||
override fun openFileChooser(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams) {
|
||||
override fun openFileChooser(
|
||||
filePathCallback: ValueCallback<Array<Uri>?>,
|
||||
fileChooserParams: WebChromeClient.FileChooserParams
|
||||
) {
|
||||
openMediaPicker(filePathCallback, fileChooserParams)
|
||||
}
|
||||
|
||||
@ -247,9 +292,11 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
|
||||
menuInflater.inflate(R.menu.menu_web, menu)
|
||||
overlayContext?.onMenuCreate(this, menu)
|
||||
toolbar.tint(Prefs.iconColor)
|
||||
setMenuIcons(menu, Prefs.iconColor,
|
||||
R.id.action_share to CommunityMaterial.Icon2.cmd_share,
|
||||
R.id.action_copy_link to GoogleMaterial.Icon.gmd_content_copy)
|
||||
setMenuIcons(
|
||||
menu, Prefs.iconColor,
|
||||
R.id.action_share to CommunityMaterial.Icon2.cmd_share,
|
||||
R.id.action_copy_link to GoogleMaterial.Icon.gmd_content_copy
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -270,5 +317,4 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc
|
||||
*/
|
||||
override var videoViewer: FrostVideoViewer? = null
|
||||
override val lowerVideoPadding: PointF = PointF(0f, 0f)
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
/**
|
||||
@ -21,10 +37,8 @@ interface DynamicUiContract {
|
||||
*/
|
||||
fun reloadTextSize()
|
||||
|
||||
|
||||
/**
|
||||
* Change text size without propagation
|
||||
*/
|
||||
fun reloadTextSizeSelf()
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import android.app.Activity
|
||||
@ -17,12 +33,19 @@ import com.pitchedapps.frost.utils.L
|
||||
const val MEDIA_CHOOSER_RESULT = 67
|
||||
|
||||
interface FileChooserActivityContract {
|
||||
fun openFileChooser(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams)
|
||||
fun openFileChooser(
|
||||
filePathCallback: ValueCallback<Array<Uri>?>,
|
||||
fileChooserParams: WebChromeClient.FileChooserParams
|
||||
)
|
||||
}
|
||||
|
||||
interface FileChooserContract {
|
||||
var filePathCallback: ValueCallback<Array<Uri>?>?
|
||||
fun Activity.openMediaPicker(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams)
|
||||
fun Activity.openMediaPicker(
|
||||
filePathCallback: ValueCallback<Array<Uri>?>,
|
||||
fileChooserParams: WebChromeClient.FileChooserParams
|
||||
)
|
||||
|
||||
fun Activity.onActivityResultWeb(requestCode: Int, resultCode: Int, intent: Intent?): Boolean
|
||||
}
|
||||
|
||||
@ -30,7 +53,10 @@ class FileChooserDelegate : FileChooserContract {
|
||||
|
||||
override var filePathCallback: ValueCallback<Array<Uri>?>? = null
|
||||
|
||||
override fun Activity.openMediaPicker(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams) {
|
||||
override fun Activity.openMediaPicker(
|
||||
filePathCallback: ValueCallback<Array<Uri>?>,
|
||||
fileChooserParams: WebChromeClient.FileChooserParams
|
||||
) {
|
||||
kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) { granted, _ ->
|
||||
if (!granted) {
|
||||
filePathCallback.onReceiveValue(null)
|
||||
@ -52,5 +78,4 @@ class FileChooserDelegate : FileChooserContract {
|
||||
filePathCallback = null
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import android.view.View
|
||||
@ -23,7 +39,6 @@ interface FrostContentContainer {
|
||||
* Update toolbar title
|
||||
*/
|
||||
fun setTitle(title: String)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +99,6 @@ interface FrostContentParent : DynamicUiContract {
|
||||
* For those cases, we will return false to stop it
|
||||
*/
|
||||
fun registerTransition(urlChanged: Boolean, animate: Boolean): Boolean
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,5 +161,4 @@ interface FrostContentCore : DynamicUiContract {
|
||||
* Signal destruction to release some content manually
|
||||
*/
|
||||
fun destroy()
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
@ -27,5 +43,4 @@ interface FrostObservables {
|
||||
other.progressObservable = progressObservable
|
||||
other.titleObservable = titleObservable
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import android.view.View
|
||||
@ -18,12 +34,11 @@ interface FrostThemable {
|
||||
fun reloadTheme()
|
||||
|
||||
fun setTextColors(color: Int, vararg textViews: TextView?) =
|
||||
themeViews(color, *textViews) { setTextColor(it) }
|
||||
themeViews(color, *textViews) { setTextColor(it) }
|
||||
|
||||
fun setBackgrounds(color: Int, vararg views: View?) =
|
||||
themeViews(color, *views) { setBackgroundColor(it) }
|
||||
themeViews(color, *views) { setBackgroundColor(it) }
|
||||
|
||||
fun <T : View> themeViews(color: Int, vararg views: T?, action: T.(Int) -> Unit) =
|
||||
views.filterNotNull().forEach { it.action(color) }
|
||||
|
||||
views.filterNotNull().forEach { it.action(color) }
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.contracts
|
||||
|
||||
import android.app.Activity
|
||||
@ -49,5 +65,4 @@ interface FrameWrapper {
|
||||
setContentView(R.layout.activity_frame_wrapper)
|
||||
frameWrapper.inflate(layoutRes, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.dbflow
|
||||
|
||||
import android.os.Parcel
|
||||
@ -11,7 +27,13 @@ import com.raizlabs.android.dbflow.annotation.ConflictAction
|
||||
import com.raizlabs.android.dbflow.annotation.Database
|
||||
import com.raizlabs.android.dbflow.annotation.PrimaryKey
|
||||
import com.raizlabs.android.dbflow.annotation.Table
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.*
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.async
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.delete
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.eq
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.from
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.save
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.select
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.where
|
||||
import com.raizlabs.android.dbflow.structure.BaseModel
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@ -30,7 +52,8 @@ object CookiesDb {
|
||||
|
||||
@PaperParcel
|
||||
@Table(database = CookiesDb::class, allFields = true, primaryKeyConflict = ConflictAction.REPLACE)
|
||||
data class CookieModel(@PrimaryKey var id: Long = -1L, var name: String? = null, var cookie: String? = null) : BaseModel(), Parcelable {
|
||||
data class CookieModel(@PrimaryKey var id: Long = -1L, var name: String? = null, var cookie: String? = null) :
|
||||
BaseModel(), Parcelable {
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR = PaperParcelCookieModel.CREATOR
|
||||
@ -40,18 +63,22 @@ data class CookieModel(@PrimaryKey var id: Long = -1L, var name: String? = null,
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) = PaperParcelCookieModel.writeToParcel(this, dest, flags)
|
||||
}
|
||||
|
||||
fun loadFbCookie(id: Long): CookieModel? = (select from CookieModel::class where (CookieModel_Table.id eq id)).querySingle()
|
||||
fun loadFbCookie(name: String): CookieModel? = (select from CookieModel::class where (CookieModel_Table.name eq name)).querySingle()
|
||||
fun loadFbCookie(id: Long): CookieModel? =
|
||||
(select from CookieModel::class where (CookieModel_Table.id eq id)).querySingle()
|
||||
|
||||
fun loadFbCookie(name: String): CookieModel? =
|
||||
(select from CookieModel::class where (CookieModel_Table.name eq name)).querySingle()
|
||||
|
||||
/**
|
||||
* Loads cookies sorted by name
|
||||
*/
|
||||
fun loadFbCookiesAsync(callback: (cookies: List<CookieModel>) -> Unit) {
|
||||
(select from CookieModel::class).orderBy(CookieModel_Table.name, true).async().queryListResultCallback { _, tResult -> callback(tResult) }.execute()
|
||||
(select from CookieModel::class).orderBy(CookieModel_Table.name, true).async()
|
||||
.queryListResultCallback { _, tResult -> callback(tResult) }.execute()
|
||||
}
|
||||
|
||||
fun loadFbCookiesSync(): List<CookieModel> = (select from CookieModel::class).orderBy(CookieModel_Table.name, true).queryList()
|
||||
|
||||
fun loadFbCookiesSync(): List<CookieModel> =
|
||||
(select from CookieModel::class).orderBy(CookieModel_Table.name, true).queryList()
|
||||
|
||||
inline fun saveFbCookie(cookie: CookieModel, crossinline callback: (() -> Unit) = {}) {
|
||||
cookie.async save {
|
||||
@ -69,24 +96,24 @@ fun removeCookie(id: Long) {
|
||||
}
|
||||
|
||||
inline fun CookieModel.fetchUsername(crossinline callback: (String) -> Unit): Disposable =
|
||||
ReactiveNetwork.checkInternetConnectivity().subscribeOn(Schedulers.io()).subscribe { yes, _ ->
|
||||
if (!yes) return@subscribe callback("")
|
||||
var result = ""
|
||||
try {
|
||||
result = frostJsoup(cookie, FbItem.PROFILE.url).title()
|
||||
L.d { "Fetch username found" }
|
||||
} catch (e: Exception) {
|
||||
if (e !is UnknownHostException)
|
||||
e.logFrostEvent("Fetch username failed")
|
||||
} finally {
|
||||
if (result.isBlank() && (name?.isNotBlank() == true)) {
|
||||
callback(name!!)
|
||||
return@subscribe
|
||||
}
|
||||
if (name != result) {
|
||||
name = result
|
||||
saveFbCookie(this@fetchUsername)
|
||||
}
|
||||
callback(result)
|
||||
ReactiveNetwork.checkInternetConnectivity().subscribeOn(Schedulers.io()).subscribe { yes, _ ->
|
||||
if (!yes) return@subscribe callback("")
|
||||
var result = ""
|
||||
try {
|
||||
result = frostJsoup(cookie, FbItem.PROFILE.url).title()
|
||||
L.d { "Fetch username found" }
|
||||
} catch (e: Exception) {
|
||||
if (e !is UnknownHostException)
|
||||
e.logFrostEvent("Fetch username failed")
|
||||
} finally {
|
||||
if (result.isBlank() && (name?.isNotBlank() == true)) {
|
||||
callback(name!!)
|
||||
return@subscribe
|
||||
}
|
||||
if (name != result) {
|
||||
name = result
|
||||
saveFbCookie(this@fetchUsername)
|
||||
}
|
||||
callback(result)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.dbflow
|
||||
|
||||
import android.content.Context
|
||||
@ -14,7 +30,6 @@ object DbUtils {
|
||||
fun db(name: String) = FlowManager.getDatabase(name)
|
||||
fun dbName(name: String) = "$name.db"
|
||||
fun deleteDatabase(c: Context, name: String) = c.deleteDatabase(dbName(name))
|
||||
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> List<T>.replace(dbName: String) {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.dbflow
|
||||
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
|
@ -1,8 +1,33 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.dbflow
|
||||
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.raizlabs.android.dbflow.annotation.*
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.*
|
||||
import com.raizlabs.android.dbflow.annotation.ConflictAction
|
||||
import com.raizlabs.android.dbflow.annotation.Database
|
||||
import com.raizlabs.android.dbflow.annotation.Migration
|
||||
import com.raizlabs.android.dbflow.annotation.PrimaryKey
|
||||
import com.raizlabs.android.dbflow.annotation.Table
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.async
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.eq
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.from
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.save
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.select
|
||||
import com.raizlabs.android.dbflow.kotlinextensions.where
|
||||
import com.raizlabs.android.dbflow.sql.SQLiteType
|
||||
import com.raizlabs.android.dbflow.sql.migration.AlterTableMigration
|
||||
import com.raizlabs.android.dbflow.structure.BaseModel
|
||||
@ -18,7 +43,8 @@ object NotificationDb {
|
||||
}
|
||||
|
||||
@Migration(version = 2, database = NotificationDb::class)
|
||||
class NotificationMigration2(modelClass: Class<NotificationModel>) : AlterTableMigration<NotificationModel>(modelClass) {
|
||||
class NotificationMigration2(modelClass: Class<NotificationModel>) :
|
||||
AlterTableMigration<NotificationModel>(modelClass) {
|
||||
override fun onPreMigrate() {
|
||||
super.onPreMigrate()
|
||||
addColumn(SQLiteType.INTEGER, "epochIm")
|
||||
@ -27,11 +53,14 @@ class NotificationMigration2(modelClass: Class<NotificationModel>) : 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 = -1L) : BaseModel()
|
||||
data class NotificationModel(
|
||||
@PrimaryKey var id: Long = -1L,
|
||||
var epoch: Long = -1L,
|
||||
var epochIm: Long = -1L
|
||||
) : BaseModel()
|
||||
|
||||
fun lastNotificationTime(id: Long): NotificationModel = (select from NotificationModel::class where (NotificationModel_Table.id eq id)).querySingle()
|
||||
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) {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.debugger
|
||||
|
||||
import ca.allanwang.kau.logging.KauLoggerExtension
|
||||
@ -32,21 +48,23 @@ import java.util.zip.ZipOutputStream
|
||||
*
|
||||
* Inspired by <a href="https://github.com/JonasCz/save-for-offline">Save for Offline</a>
|
||||
*/
|
||||
class OfflineWebsite(private val url: String,
|
||||
private val cookie: String = "",
|
||||
baseUrl: String? = null,
|
||||
private val html: String? = null,
|
||||
/**
|
||||
* Directory that holds all the files
|
||||
*/
|
||||
val baseDir: File,
|
||||
private val userAgent: String = USER_AGENT_BASIC) {
|
||||
class OfflineWebsite(
|
||||
private val url: String,
|
||||
private val cookie: String = "",
|
||||
baseUrl: String? = null,
|
||||
private val html: String? = null,
|
||||
/**
|
||||
* Directory that holds all the files
|
||||
*/
|
||||
val baseDir: File,
|
||||
private val userAgent: String = USER_AGENT_BASIC
|
||||
) {
|
||||
|
||||
/**
|
||||
* Supplied url without the queries
|
||||
*/
|
||||
private val baseUrl = (baseUrl ?: url.substringBefore("?")
|
||||
.substringBefore(".com")).trim('/')
|
||||
.substringBefore(".com")).trim('/')
|
||||
|
||||
private val mainFile = File(baseDir, "index.html")
|
||||
private val assetDir = File(baseDir, "assets")
|
||||
@ -67,11 +85,11 @@ class OfflineWebsite(private val url: String,
|
||||
private val cssQueue = mutableSetOf<String>()
|
||||
|
||||
private fun request(url: String) = Request.Builder()
|
||||
.header("Cookie", cookie)
|
||||
.header("User-Agent", userAgent)
|
||||
.url(url)
|
||||
.get()
|
||||
.call()
|
||||
.header("Cookie", cookie)
|
||||
.header("User-Agent", userAgent)
|
||||
.url(url)
|
||||
.get()
|
||||
.call()
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
@ -94,7 +112,6 @@ class OfflineWebsite(private val url: String,
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
|
||||
if (!assetDir.createFreshDir()) {
|
||||
L.e { "Could not create ${assetDir.absolutePath}" }
|
||||
return callback(false)
|
||||
@ -245,8 +262,10 @@ class OfflineWebsite(private val url: String,
|
||||
}
|
||||
})
|
||||
|
||||
private inline fun <T> String.downloadUrl(fallback: () -> T,
|
||||
action: (file: File, body: ResponseBody) -> T): T {
|
||||
private inline fun <T> String.downloadUrl(
|
||||
fallback: () -> T,
|
||||
action: (file: File, body: ResponseBody) -> T
|
||||
): T {
|
||||
|
||||
val file = File(assetDir, fileName())
|
||||
if (!file.createNewFile()) {
|
||||
@ -289,11 +308,10 @@ class OfflineWebsite(private val url: String,
|
||||
if (mapped != null) return mapped
|
||||
|
||||
val candidate = substringBefore("?").trim('/')
|
||||
.substringAfterLast("/").shorten()
|
||||
.substringAfterLast("/").shorten()
|
||||
|
||||
val index = atomicInt.getAndIncrement()
|
||||
|
||||
|
||||
var newUrl = "a${index}_$candidate"
|
||||
|
||||
/**
|
||||
@ -308,10 +326,10 @@ class OfflineWebsite(private val url: String,
|
||||
}
|
||||
|
||||
private fun String.shorten() =
|
||||
if (length <= 10) this else substring(length - 10)
|
||||
if (length <= 10) this else substring(length - 10)
|
||||
|
||||
private fun Set<String>.clean(): List<String> =
|
||||
filter(String::isNotBlank).filter { it.startsWith("http") }
|
||||
filter(String::isNotBlank).filter { it.startsWith("http") }
|
||||
|
||||
private fun reset() {
|
||||
cancelled = false
|
||||
@ -326,5 +344,4 @@ class OfflineWebsite(private val url: String,
|
||||
compositeDisposable.dispose()
|
||||
L.v { "Request cancelled" }
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.enums
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.enums
|
||||
|
||||
import com.pitchedapps.frost.R
|
||||
@ -7,20 +23,21 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
* Created by Allan Wang on 2017-08-19.
|
||||
*/
|
||||
enum class MainActivityLayout(
|
||||
val titleRes: Int,
|
||||
val layoutRes: Int,
|
||||
val backgroundColor: () -> Int,
|
||||
val iconColor: () -> Int) {
|
||||
val titleRes: Int,
|
||||
val layoutRes: Int,
|
||||
val backgroundColor: () -> Int,
|
||||
val iconColor: () -> Int
|
||||
) {
|
||||
|
||||
TOP_BAR(R.string.top_bar,
|
||||
R.layout.activity_main,
|
||||
{ Prefs.headerColor },
|
||||
{ Prefs.iconColor }),
|
||||
R.layout.activity_main,
|
||||
{ Prefs.headerColor },
|
||||
{ Prefs.iconColor }),
|
||||
|
||||
BOTTOM_BAR(R.string.bottom_bar,
|
||||
R.layout.activity_main_bottom_tabs,
|
||||
{ Prefs.bgColor },
|
||||
{ Prefs.textColor });
|
||||
R.layout.activity_main_bottom_tabs,
|
||||
{ Prefs.bgColor },
|
||||
{ Prefs.textColor });
|
||||
|
||||
companion object {
|
||||
val values = values() //save one instance
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.enums
|
||||
|
||||
import android.content.Context
|
||||
@ -52,9 +68,10 @@ enum class OverlayContext(private val menuItem: FrostMenuItem?) : EnumBundle<Ove
|
||||
* Frame for an injectable menu item
|
||||
*/
|
||||
class FrostMenuItem(
|
||||
val id: Int,
|
||||
val fbItem: FbItem,
|
||||
val showAsAction: Int = MenuItem.SHOW_AS_ACTION_IF_ROOM) {
|
||||
val id: Int,
|
||||
val fbItem: FbItem,
|
||||
val showAsAction: Int = MenuItem.SHOW_AS_ACTION_IF_ROOM
|
||||
) {
|
||||
fun addToMenu(context: Context, menu: Menu, index: Int) {
|
||||
val item = menu.add(Menu.NONE, id, index, fbItem.titleId)
|
||||
item.icon = fbItem.icon.toDrawable(context, 18)
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.enums
|
||||
|
||||
import android.content.Context
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.enums
|
||||
|
||||
import android.graphics.Color
|
||||
@ -14,61 +30,63 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
const val FACEBOOK_BLUE = 0xff3b5998.toInt()
|
||||
const val BLUE_LIGHT = 0xff5d86dd.toInt()
|
||||
|
||||
enum class Theme(@StringRes val textRes: Int,
|
||||
val injector: InjectorContract,
|
||||
private val textColorGetter: () -> Int,
|
||||
private val accentColorGetter: () -> Int,
|
||||
private val backgroundColorGetter: () -> Int,
|
||||
private val headerColorGetter: () -> Int,
|
||||
private val iconColorGetter: () -> Int) {
|
||||
enum class Theme(
|
||||
@StringRes val textRes: Int,
|
||||
val injector: InjectorContract,
|
||||
private val textColorGetter: () -> Int,
|
||||
private val accentColorGetter: () -> Int,
|
||||
private val backgroundColorGetter: () -> Int,
|
||||
private val headerColorGetter: () -> Int,
|
||||
private val iconColorGetter: () -> Int
|
||||
) {
|
||||
|
||||
DEFAULT(R.string.kau_default,
|
||||
JsActions.EMPTY,
|
||||
{ 0xde000000.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ 0xfffafafa.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ Color.WHITE }),
|
||||
JsActions.EMPTY,
|
||||
{ 0xde000000.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ 0xfffafafa.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ Color.WHITE }),
|
||||
|
||||
LIGHT(R.string.kau_light,
|
||||
CssAssets.MATERIAL_LIGHT,
|
||||
{ 0xde000000.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ 0xfffafafa.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ Color.WHITE }),
|
||||
CssAssets.MATERIAL_LIGHT,
|
||||
{ 0xde000000.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ 0xfffafafa.toInt() },
|
||||
{ FACEBOOK_BLUE },
|
||||
{ Color.WHITE }),
|
||||
|
||||
DARK(R.string.kau_dark,
|
||||
CssAssets.MATERIAL_DARK,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ 0xff303030.toInt() },
|
||||
{ 0xff2e4b86.toInt() },
|
||||
{ Color.WHITE }),
|
||||
CssAssets.MATERIAL_DARK,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ 0xff303030.toInt() },
|
||||
{ 0xff2e4b86.toInt() },
|
||||
{ Color.WHITE }),
|
||||
|
||||
AMOLED(R.string.kau_amoled,
|
||||
CssAssets.MATERIAL_AMOLED,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ Color.BLACK },
|
||||
{ Color.BLACK },
|
||||
{ Color.WHITE }),
|
||||
CssAssets.MATERIAL_AMOLED,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ Color.BLACK },
|
||||
{ Color.BLACK },
|
||||
{ Color.WHITE }),
|
||||
|
||||
GLASS(R.string.kau_glass,
|
||||
CssAssets.MATERIAL_GLASS,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ 0x80000000.toInt() },
|
||||
{ 0xb3000000.toInt() },
|
||||
{ Color.WHITE }),
|
||||
CssAssets.MATERIAL_GLASS,
|
||||
{ Color.WHITE },
|
||||
{ BLUE_LIGHT },
|
||||
{ 0x80000000.toInt() },
|
||||
{ 0xb3000000.toInt() },
|
||||
{ Color.WHITE }),
|
||||
|
||||
CUSTOM(R.string.kau_custom,
|
||||
CssAssets.CUSTOM,
|
||||
{ Prefs.customTextColor },
|
||||
{ Prefs.customAccentColor },
|
||||
{ Prefs.customBackgroundColor },
|
||||
{ Prefs.customHeaderColor },
|
||||
{ Prefs.customIconColor });
|
||||
CssAssets.CUSTOM,
|
||||
{ Prefs.customTextColor },
|
||||
{ Prefs.customAccentColor },
|
||||
{ Prefs.customBackgroundColor },
|
||||
{ Prefs.customHeaderColor },
|
||||
{ Prefs.customIconColor });
|
||||
|
||||
val textColor: Int
|
||||
get() = textColorGetter()
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook
|
||||
|
||||
/**
|
||||
@ -12,9 +28,12 @@ fun profilePictureUrl(id: Long) = "https://graph.facebook.com/$id/picture?type=l
|
||||
const val FB_LOGIN_URL = "${FB_URL_BASE}login"
|
||||
const val FB_HOME_URL = "${FB_URL_BASE}home.php"
|
||||
|
||||
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_OLD = "Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.4633 Mobile Safari/537.10+"
|
||||
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"
|
||||
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_OLD =
|
||||
"Mozilla/5.0 (Linux; Android 6.0) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.4633 Mobile Safari/537.10+"
|
||||
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"
|
||||
const val USER_AGENT_BASIC = USER_AGENT_MESSENGER
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook
|
||||
|
||||
import android.app.Activity
|
||||
@ -38,13 +54,13 @@ object FbCookie {
|
||||
val cookies = cookie.split(";").map { Pair(it, SingleSubject.create<Boolean>()) }
|
||||
cookies.forEach { (cookie, callback) -> setCookie(FB_URL_BASE, cookie) { callback.onSuccess(it) } }
|
||||
Observable.zip<Boolean, Unit>(cookies.map { (_, callback) -> callback.toObservable() }) {}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
callback?.invoke()
|
||||
L.d { "Cookies set" }
|
||||
L._d { cookie }
|
||||
flush()
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
callback?.invoke()
|
||||
L.d { "Cookies set" }
|
||||
L._d { cookie }
|
||||
flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
@ -15,10 +31,10 @@ import com.pitchedapps.frost.utils.EnumBundleCompanion
|
||||
import com.pitchedapps.frost.utils.EnumCompanion
|
||||
|
||||
enum class FbItem(
|
||||
@StringRes val titleId: Int,
|
||||
val icon: IIcon,
|
||||
relativeUrl: String,
|
||||
val fragmentCreator: () -> BaseFragment = ::WebFragment
|
||||
@StringRes val titleId: Int,
|
||||
val icon: IIcon,
|
||||
relativeUrl: String,
|
||||
val fragmentCreator: () -> BaseFragment = ::WebFragment
|
||||
) : EnumBundle<FbItem> {
|
||||
|
||||
ACTIVITY_LOG(R.string.activity_log, GoogleMaterial.Icon.gmd_list, "me/allactivity"),
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook
|
||||
|
||||
/**
|
||||
@ -29,4 +45,3 @@ val FB_IMAGE_ID_MATCHER: Regex = Regex("fbcdn.*?/[0-9]+_([0-9]+)_")
|
||||
val FB_REDIRECT_URL_MATCHER: Regex = Regex("url=(.*?fbcdn.*?)\"")
|
||||
|
||||
operator fun MatchResult?.get(groupIndex: Int) = this?.groupValues?.get(groupIndex)
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook
|
||||
|
||||
import com.pitchedapps.frost.utils.L
|
||||
@ -91,13 +107,13 @@ class FbUrlFormatter(url: String) {
|
||||
* That shouldn't break anything
|
||||
*/
|
||||
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=",
|
||||
VIDEO_REDIRECT
|
||||
"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=",
|
||||
VIDEO_REDIRECT
|
||||
)
|
||||
|
||||
/**
|
||||
@ -108,13 +124,13 @@ class FbUrlFormatter(url: String) {
|
||||
val discardableQueries = arrayOf("ref", "refid", "SharedWith")
|
||||
|
||||
val converter = listOf(
|
||||
"\\3C " to "%3C", "\\3E " to "%3E", "\\23 " to "%23", "\\25 " to "%25",
|
||||
"\\7B " to "%7B", "\\7D " to "%7D", "\\7C " to "%7C", "\\5C " to "%5C",
|
||||
"\\5E " to "%5E", "\\7E " to "%7E", "\\5B " to "%5B", "\\5D " to "%5D",
|
||||
"\\60 " to "%60", "\\3B " to "%3B", "\\2F " to "%2F", "\\3F " to "%3F",
|
||||
"\\3A " to "%3A", "\\40 " to "%40", "\\3D " to "%3D", "\\26 " to "%26",
|
||||
"\\24 " to "%24", "\\2B " to "%2B", "\\22 " to "%22", "\\2C " to "%2C",
|
||||
"\\20 " to "%20"
|
||||
"\\3C " to "%3C", "\\3E " to "%3E", "\\23 " to "%23", "\\25 " to "%25",
|
||||
"\\7B " to "%7B", "\\7D " to "%7D", "\\7C " to "%7C", "\\5C " to "%5C",
|
||||
"\\5E " to "%5E", "\\7E " to "%7E", "\\5B " to "%5B", "\\5D " to "%5D",
|
||||
"\\60 " to "%60", "\\3B " to "%3B", "\\2F " to "%2F", "\\3F " to "%3F",
|
||||
"\\3A " to "%3A", "\\40 " to "%40", "\\3D " to "%3D", "\\26 " to "%26",
|
||||
"\\24 " to "%24", "\\2B " to "%2B", "\\22 " to "%22", "\\2C " to "%2C",
|
||||
"\\20 " to "%20"
|
||||
)
|
||||
}
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.parsers
|
||||
|
||||
import com.pitchedapps.frost.dbflow.CookieModel
|
||||
@ -54,7 +70,6 @@ interface FrostParser<out T : Any> {
|
||||
* Call parsing with given data
|
||||
*/
|
||||
fun parseFromData(cookie: String?, text: String): ParseResponse<T>?
|
||||
|
||||
}
|
||||
|
||||
const val FALLBACK_TIME_MOD = 1000000
|
||||
@ -92,7 +107,7 @@ internal abstract class FrostParserBase<out T : Any>(private val redirectToText:
|
||||
}
|
||||
|
||||
final override fun parseFromUrl(cookie: String?, url: String): ParseResponse<T>? =
|
||||
parse(cookie, frostJsoup(cookie, url))
|
||||
parse(cookie, frostJsoup(cookie, url))
|
||||
|
||||
override fun parse(cookie: String?, document: Document): ParseResponse<T>? {
|
||||
cookie ?: return null
|
||||
@ -109,14 +124,14 @@ internal abstract class FrostParserBase<out T : Any>(private val redirectToText:
|
||||
* Returns the formatted url, or an empty string if nothing was found
|
||||
*/
|
||||
protected fun Element.getInnerImgStyle(): String? =
|
||||
select("i.img[style*=url]").getStyleUrl()
|
||||
select("i.img[style*=url]").getStyleUrl()
|
||||
|
||||
protected fun Elements.getStyleUrl(): String? =
|
||||
FB_CSS_URL_MATCHER.find(attr("style"))[1]?.formattedFbUrl
|
||||
FB_CSS_URL_MATCHER.find(attr("style"))[1]?.formattedFbUrl
|
||||
|
||||
protected open fun textToDoc(text: String): Document? =
|
||||
if (!redirectToText) Jsoup.parse(text)
|
||||
else throw RuntimeException("${this::class.java.simpleName} requires text redirect but did not implement textToDoc")
|
||||
if (!redirectToText) Jsoup.parse(text)
|
||||
else throw RuntimeException("${this::class.java.simpleName} requires text redirect but did not implement textToDoc")
|
||||
|
||||
protected fun parseLink(element: Element?): FrostLink? {
|
||||
val a = element?.getElementsByTag("a")?.first() ?: return null
|
||||
|
@ -1,7 +1,27 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.parsers
|
||||
|
||||
import com.pitchedapps.frost.dbflow.CookieModel
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FB_EPOCH_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_MESSAGE_NOTIF_ID_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.facebook.formattedFbUrl
|
||||
import com.pitchedapps.frost.facebook.get
|
||||
import com.pitchedapps.frost.services.NotificationContent
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
@ -19,12 +39,12 @@ import org.jsoup.nodes.Element
|
||||
object MessageParser : FrostParser<FrostMessages> by MessageParserImpl() {
|
||||
|
||||
fun queryUser(cookie: String?, name: String) = parseFromUrl(cookie, "${FbItem.MESSAGES.url}/?q=$name")
|
||||
|
||||
}
|
||||
|
||||
data class FrostMessages(val threads: List<FrostThread>,
|
||||
val seeMore: FrostLink?,
|
||||
val extraLinks: List<FrostLink>
|
||||
data class FrostMessages(
|
||||
val threads: List<FrostThread>,
|
||||
val seeMore: FrostLink?,
|
||||
val extraLinks: List<FrostLink>
|
||||
) : ParseNotification {
|
||||
override fun toString() = StringBuilder().apply {
|
||||
append("FrostMessages {\n")
|
||||
@ -35,19 +55,19 @@ data class FrostMessages(val threads: List<FrostThread>,
|
||||
}.toString()
|
||||
|
||||
override fun getUnreadNotifications(data: CookieModel) =
|
||||
threads.asSequence().filter(FrostThread::unread).map {
|
||||
with(it) {
|
||||
NotificationContent(
|
||||
data = data,
|
||||
id = id,
|
||||
href = url,
|
||||
title = title,
|
||||
text = content ?: "",
|
||||
timestamp = time,
|
||||
profileUrl = img
|
||||
)
|
||||
}
|
||||
}.toList()
|
||||
threads.asSequence().filter(FrostThread::unread).map {
|
||||
with(it) {
|
||||
NotificationContent(
|
||||
data = data,
|
||||
id = id,
|
||||
href = url,
|
||||
title = title,
|
||||
text = content ?: "",
|
||||
timestamp = time,
|
||||
profileUrl = img
|
||||
)
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,14 +78,16 @@ data class FrostMessages(val threads: List<FrostThread>,
|
||||
* [unread] true if image is unread, false otherwise
|
||||
* [content] optional string for thread
|
||||
*/
|
||||
data class FrostThread(val id: Long,
|
||||
val img: String?,
|
||||
val title: String,
|
||||
val time: Long,
|
||||
val url: String,
|
||||
val unread: Boolean,
|
||||
val content: String?,
|
||||
val contentImgUrl: String?)
|
||||
data class FrostThread(
|
||||
val id: Long,
|
||||
val img: String?,
|
||||
val title: String,
|
||||
val time: Long,
|
||||
val url: String,
|
||||
val unread: Boolean,
|
||||
val content: String?,
|
||||
val contentImgUrl: String?
|
||||
)
|
||||
|
||||
private class MessageParserImpl : FrostParserBase<FrostMessages>(true) {
|
||||
|
||||
@ -92,11 +114,12 @@ private class MessageParserImpl : FrostParserBase<FrostMessages>(true) {
|
||||
|
||||
override fun parseImpl(doc: Document): FrostMessages? {
|
||||
val threadList = doc.getElementById("threadlist_rows") ?: return null
|
||||
val threads: List<FrostThread> = threadList.getElementsByAttributeValueMatching("id", ".*${FB_MESSAGE_NOTIF_ID_MATCHER.pattern}.*")
|
||||
val threads: List<FrostThread> =
|
||||
threadList.getElementsByAttributeValueMatching("id", ".*${FB_MESSAGE_NOTIF_ID_MATCHER.pattern}.*")
|
||||
.mapNotNull(this::parseMessage)
|
||||
val seeMore = parseLink(doc.getElementById("see_older_threads"))
|
||||
val extraLinks = threadList.nextElementSibling().select("a")
|
||||
.mapNotNull(this::parseLink)
|
||||
.mapNotNull(this::parseLink)
|
||||
return FrostMessages(threads, seeMore, extraLinks)
|
||||
}
|
||||
|
||||
@ -106,21 +129,20 @@ private class MessageParserImpl : FrostParserBase<FrostMessages>(true) {
|
||||
val epoch = FB_EPOCH_MATCHER.find(abbr.attr("data-store"))[1]?.toLongOrNull() ?: -1L
|
||||
//fetch id
|
||||
val id = FB_MESSAGE_NOTIF_ID_MATCHER.find(element.id())[1]?.toLongOrNull()
|
||||
?: System.currentTimeMillis() % FALLBACK_TIME_MOD
|
||||
?: System.currentTimeMillis() % FALLBACK_TIME_MOD
|
||||
val snippet = element.select("span.snippet").firstOrNull()
|
||||
val content = snippet?.text()?.trim()
|
||||
val contentImg = snippet?.select("i[style*=url]")?.getStyleUrl()
|
||||
val img = element.getInnerImgStyle()
|
||||
return FrostThread(
|
||||
id = id,
|
||||
img = img,
|
||||
title = a.text(),
|
||||
time = epoch,
|
||||
url = a.attr("href").formattedFbUrl,
|
||||
unread = !element.hasClass("acw"),
|
||||
content = content,
|
||||
contentImgUrl = contentImg
|
||||
id = id,
|
||||
img = img,
|
||||
title = a.text(),
|
||||
time = epoch,
|
||||
url = a.attr("href").formattedFbUrl,
|
||||
unread = !element.hasClass("acw"),
|
||||
content = content,
|
||||
contentImgUrl = contentImg
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,27 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.parsers
|
||||
|
||||
import com.pitchedapps.frost.dbflow.CookieModel
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FB_EPOCH_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_NOTIF_ID_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.facebook.formattedFbUrl
|
||||
import com.pitchedapps.frost.facebook.get
|
||||
import com.pitchedapps.frost.services.NotificationContent
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
@ -13,8 +33,8 @@ import org.jsoup.nodes.Element
|
||||
object NotifParser : FrostParser<FrostNotifs> by NotifParserImpl()
|
||||
|
||||
data class FrostNotifs(
|
||||
val notifs: List<FrostNotif>,
|
||||
val seeMore: FrostLink?
|
||||
val notifs: List<FrostNotif>,
|
||||
val seeMore: FrostLink?
|
||||
) : ParseNotification {
|
||||
override fun toString() = StringBuilder().apply {
|
||||
append("FrostNotifs {\n")
|
||||
@ -24,19 +44,19 @@ data class FrostNotifs(
|
||||
}.toString()
|
||||
|
||||
override fun getUnreadNotifications(data: CookieModel) =
|
||||
notifs.asSequence().filter(FrostNotif::unread).map {
|
||||
with(it) {
|
||||
NotificationContent(
|
||||
data = data,
|
||||
id = id,
|
||||
href = url,
|
||||
title = null,
|
||||
text = content,
|
||||
timestamp = time,
|
||||
profileUrl = img
|
||||
)
|
||||
}
|
||||
}.toList()
|
||||
notifs.asSequence().filter(FrostNotif::unread).map {
|
||||
with(it) {
|
||||
NotificationContent(
|
||||
data = data,
|
||||
id = id,
|
||||
href = url,
|
||||
title = null,
|
||||
text = content,
|
||||
timestamp = time,
|
||||
profileUrl = img
|
||||
)
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,14 +69,16 @@ data class FrostNotifs(
|
||||
* [timeString] text version of time from Facebook
|
||||
* [thumbnailUrl] optional thumbnail url if existent
|
||||
*/
|
||||
data class FrostNotif(val id: Long,
|
||||
val img: String?,
|
||||
val time: Long,
|
||||
val url: String,
|
||||
val unread: Boolean,
|
||||
val content: String,
|
||||
val timeString: String,
|
||||
val thumbnailUrl: String?)
|
||||
data class FrostNotif(
|
||||
val id: Long,
|
||||
val img: String?,
|
||||
val time: Long,
|
||||
val url: String,
|
||||
val unread: Boolean,
|
||||
val content: String,
|
||||
val timeString: String,
|
||||
val thumbnailUrl: String?
|
||||
)
|
||||
|
||||
private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) {
|
||||
|
||||
@ -67,8 +89,8 @@ private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) {
|
||||
override fun parseImpl(doc: Document): FrostNotifs? {
|
||||
val notificationList = doc.getElementById("notifications_list") ?: return null
|
||||
val notifications = notificationList
|
||||
.getElementsByAttributeValueMatching("id", ".*${FB_NOTIF_ID_MATCHER.pattern}.*")
|
||||
.mapNotNull(this::parseNotif)
|
||||
.getElementsByAttributeValueMatching("id", ".*${FB_NOTIF_ID_MATCHER.pattern}.*")
|
||||
.mapNotNull(this::parseNotif)
|
||||
val seeMore = parseLink(doc.getElementsByAttributeValue("href", "/notifications.php?more").first())
|
||||
return FrostNotifs(notifications, seeMore)
|
||||
}
|
||||
@ -79,22 +101,20 @@ private class NotifParserImpl : FrostParserBase<FrostNotifs>(false) {
|
||||
val epoch = FB_EPOCH_MATCHER.find(abbr.attr("data-store"))[1]?.toLongOrNull() ?: -1L
|
||||
//fetch id
|
||||
val id = FB_NOTIF_ID_MATCHER.find(element.id())[1]?.toLongOrNull()
|
||||
?: System.currentTimeMillis() % FALLBACK_TIME_MOD
|
||||
?: System.currentTimeMillis() % FALLBACK_TIME_MOD
|
||||
val img = element.getInnerImgStyle()
|
||||
val timeString = abbr.text()
|
||||
val content = a.text().replace("\u00a0", " ").removeSuffix(timeString).trim() //remove
|
||||
val thumbnail = element.selectFirst("img.thumbnail")?.attr("src")
|
||||
return FrostNotif(
|
||||
id = id,
|
||||
img = img,
|
||||
time = epoch,
|
||||
url = a.attr("href").formattedFbUrl,
|
||||
unread = !element.hasClass("acw"),
|
||||
content = content,
|
||||
timeString = timeString,
|
||||
thumbnailUrl = if (thumbnail?.isNotEmpty() == true) thumbnail else null
|
||||
id = id,
|
||||
img = img,
|
||||
time = epoch,
|
||||
url = a.attr("href").formattedFbUrl,
|
||||
unread = !element.hasClass("acw"),
|
||||
content = content,
|
||||
timeString = timeString,
|
||||
thumbnailUrl = if (thumbnail?.isNotEmpty() == true) thumbnail else null
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.parsers
|
||||
|
||||
import ca.allanwang.kau.searchview.SearchItem
|
||||
@ -46,9 +62,9 @@ data class FrostSearch(val href: String, val title: String, val description: Str
|
||||
|
||||
companion object {
|
||||
fun create(href: String, title: String, description: String?) = FrostSearch(
|
||||
with(href.indexOf("?")) { if (this == -1) href else href.substring(0, this) },
|
||||
title.format(),
|
||||
description?.format()
|
||||
with(href.indexOf("?")) { if (this == -1) href else href.substring(0, this) },
|
||||
title.format(),
|
||||
description?.format()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -61,17 +77,18 @@ private class SearchParserImpl : FrostParserBase<FrostSearches>(false) {
|
||||
|
||||
override fun parseImpl(doc: Document): FrostSearches? {
|
||||
val container: Element = doc.getElementById("BrowseResultsContainer")
|
||||
?: doc.getElementById("root")
|
||||
?: return null
|
||||
?: doc.getElementById("root")
|
||||
?: return null
|
||||
/**
|
||||
*
|
||||
* Removed [data-store*=result_id]
|
||||
*/
|
||||
return FrostSearches(container.select("a.touchable[href]").filter(Element::hasText).map {
|
||||
FrostSearch.create(it.attr("href").formattedFbUrl,
|
||||
it.select("._uoi").first()?.text() ?: "",
|
||||
it.select("._1tcc").first()?.text())
|
||||
FrostSearch.create(
|
||||
it.attr("href").formattedFbUrl,
|
||||
it.select("._uoi").first()?.text() ?: "",
|
||||
it.select("._1tcc").first()?.text()
|
||||
)
|
||||
}.filter { it.title.isNotBlank() })
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,29 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.requests
|
||||
|
||||
import com.pitchedapps.frost.BuildConfig
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FB_DTSG_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_JSON_URL_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_REV_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
import com.pitchedapps.frost.facebook.FB_USER_MATCHER
|
||||
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
import com.pitchedapps.frost.facebook.get
|
||||
import com.pitchedapps.frost.rx.RxFlyweight
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import io.reactivex.Single
|
||||
@ -21,10 +43,9 @@ private class RxAuth : RxFlyweight<String, Long, RequestAuth>() {
|
||||
override fun call(input: String) = input.getAuth()
|
||||
|
||||
override fun validate(input: String, cond: Long) =
|
||||
System.currentTimeMillis() - cond < 3600000 // valid for an hour
|
||||
System.currentTimeMillis() - cond < 3600000 // valid for an hour
|
||||
|
||||
override fun cache(input: String) = System.currentTimeMillis()
|
||||
|
||||
}
|
||||
|
||||
private val auth = RxAuth()
|
||||
@ -48,10 +69,12 @@ fun String?.fbRequest(fail: () -> Unit = {}, action: RequestAuth.() -> Unit) {
|
||||
/**
|
||||
* Underlying container for all fb requests
|
||||
*/
|
||||
data class RequestAuth(val userId: Long = -1,
|
||||
val cookie: String = "",
|
||||
val fb_dtsg: String = "",
|
||||
val rev: String = "") {
|
||||
data class RequestAuth(
|
||||
val userId: Long = -1,
|
||||
val cookie: String = "",
|
||||
val fb_dtsg: String = "",
|
||||
val rev: String = ""
|
||||
) {
|
||||
val isComplete
|
||||
get() = userId > 0 && cookie.isNotEmpty() && fb_dtsg.isNotEmpty() && rev.isNotEmpty()
|
||||
}
|
||||
@ -64,8 +87,8 @@ class FrostRequest<out T : Any?>(val call: Call, private val invoke: (Call) -> T
|
||||
}
|
||||
|
||||
internal inline fun <T : Any?> RequestAuth.frostRequest(
|
||||
noinline invoke: (Call) -> T,
|
||||
builder: Request.Builder.() -> Request.Builder // to ensure we don't do anything extra at the end
|
||||
noinline invoke: (Call) -> T,
|
||||
builder: Request.Builder.() -> Request.Builder // to ensure we don't do anything extra at the end
|
||||
): FrostRequest<T> {
|
||||
val request = cookie.requestBuilder()
|
||||
request.builder()
|
||||
@ -75,8 +98,10 @@ internal inline fun <T : Any?> RequestAuth.frostRequest(
|
||||
val httpClient: OkHttpClient by lazy {
|
||||
val builder = OkHttpClient.Builder()
|
||||
if (BuildConfig.DEBUG)
|
||||
builder.addInterceptor(HttpLoggingInterceptor()
|
||||
.setLevel(HttpLoggingInterceptor.Level.BASIC))
|
||||
builder.addInterceptor(
|
||||
HttpLoggingInterceptor()
|
||||
.setLevel(HttpLoggingInterceptor.Level.BASIC)
|
||||
)
|
||||
builder.build()
|
||||
}
|
||||
|
||||
@ -97,7 +122,7 @@ internal fun List<Pair<String, Any?>>.withEmptyData(vararg key: String): List<Pa
|
||||
|
||||
internal fun String?.requestBuilder(): Request.Builder {
|
||||
val builder = Request.Builder()
|
||||
.header("User-Agent", USER_AGENT_BASIC)
|
||||
.header("User-Agent", USER_AGENT_BASIC)
|
||||
if (this != null)
|
||||
builder.header("Cookie", this)
|
||||
// .cacheControl(CacheControl.FORCE_NETWORK)
|
||||
@ -112,9 +137,9 @@ fun String.getAuth(): RequestAuth {
|
||||
val id = FB_USER_MATCHER.find(this)[1]?.toLong() ?: return auth
|
||||
auth = auth.copy(userId = id)
|
||||
val call = this.requestBuilder()
|
||||
.url(FB_URL_BASE)
|
||||
.get()
|
||||
.call()
|
||||
.url(FB_URL_BASE)
|
||||
.get()
|
||||
.call()
|
||||
call.execute().body()?.charStream()?.useLines { lines ->
|
||||
lines.forEach {
|
||||
val text = StringEscapeUtils.unescapeEcmaScript(it)
|
||||
@ -135,8 +160,10 @@ fun String.getAuth(): RequestAuth {
|
||||
return auth
|
||||
}
|
||||
|
||||
inline fun <T, reified R : Any, O> Array<T>.zip(crossinline mapper: (List<R>) -> O,
|
||||
crossinline caller: (T) -> R): Single<O> {
|
||||
inline fun <T, reified R : Any, O> Array<T>.zip(
|
||||
crossinline mapper: (List<R>) -> O,
|
||||
crossinline caller: (T) -> R
|
||||
): Single<O> {
|
||||
if (isEmpty())
|
||||
return Single.just(mapper(emptyList()))
|
||||
val singles = map { Single.fromCallable { caller(it) }.subscribeOn(Schedulers.io()) }
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.requests
|
||||
|
||||
import com.bumptech.glide.Priority
|
||||
@ -11,7 +27,11 @@ import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FB_IMAGE_ID_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_REDIRECT_URL_MATCHER
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
import com.pitchedapps.frost.facebook.formattedFbUrl
|
||||
import com.pitchedapps.frost.facebook.get
|
||||
import io.reactivex.Maybe
|
||||
import okhttp3.Call
|
||||
import okhttp3.Request
|
||||
@ -33,9 +53,9 @@ val test: () -> InputStream? = { null }
|
||||
*/
|
||||
fun String.getFullSizedImageUrl(url: String): Maybe<String?> = Maybe.fromCallable {
|
||||
val redirect = requestBuilder().url(url).get().call()
|
||||
.execute().body()?.string() ?: return@fromCallable null
|
||||
.execute().body()?.string() ?: return@fromCallable null
|
||||
return@fromCallable FB_REDIRECT_URL_MATCHER.find(redirect)[1]?.formattedFbUrl
|
||||
?: return@fromCallable null
|
||||
?: return@fromCallable null
|
||||
}.onErrorComplete()
|
||||
|
||||
/**
|
||||
@ -51,7 +71,6 @@ data class HdImageMaybe(val url: String, val cookie: String) {
|
||||
val isValid: Boolean by lazy {
|
||||
id != -1L && cookie.isNotBlank()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -69,18 +88,20 @@ class HdImageLoadingFactory : ModelLoaderFactory<HdImageMaybe, InputStream> {
|
||||
}
|
||||
|
||||
fun <T> RequestBuilder<T>.loadWithPotentialHd(model: HdImageMaybe) =
|
||||
thumbnail(clone().load(model.url))
|
||||
.load(model)
|
||||
.apply(RequestOptions().override(Target.SIZE_ORIGINAL))
|
||||
thumbnail(clone().load(model.url))
|
||||
.load(model)
|
||||
.apply(RequestOptions().override(Target.SIZE_ORIGINAL))
|
||||
|
||||
class HdImageLoading : ModelLoader<HdImageMaybe, InputStream> {
|
||||
|
||||
override fun buildLoadData(model: HdImageMaybe,
|
||||
width: Int,
|
||||
height: Int,
|
||||
options: Options): ModelLoader.LoadData<InputStream>? =
|
||||
if (!model.isValid) null
|
||||
else ModelLoader.LoadData(ObjectKey(model), HdImageFetcher(model))
|
||||
override fun buildLoadData(
|
||||
model: HdImageMaybe,
|
||||
width: Int,
|
||||
height: Int,
|
||||
options: Options
|
||||
): ModelLoader.LoadData<InputStream>? =
|
||||
if (!model.isValid) null
|
||||
else ModelLoader.LoadData(ObjectKey(model), HdImageFetcher(model))
|
||||
|
||||
override fun handles(model: HdImageMaybe) = model.isValid
|
||||
}
|
||||
@ -105,7 +126,7 @@ class HdImageFetcher(private val model: HdImageMaybe) : DataFetcher<InputStream>
|
||||
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")
|
||||
?: 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()
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.requests
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
@ -19,28 +35,27 @@ import java.io.IOException
|
||||
fun RequestAuth.getMenuData(): FrostRequest<MenuData?> {
|
||||
|
||||
val body = listOf(
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
|
||||
|
||||
return frostRequest(::parseMenu) {
|
||||
url("${FB_URL_BASE}bookmarks/flyout/body/?id=u_0_2")
|
||||
post(body.toForm())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun parseMenu(call: Call): MenuData? {
|
||||
val fullString = call.execute().body()?.string() ?: return null
|
||||
var jsonString = fullString.substringAfter("bookmarkGroups", "")
|
||||
.substringAfter("[", "")
|
||||
.substringAfter("[", "")
|
||||
|
||||
if (jsonString.isBlank()) return null
|
||||
|
||||
jsonString = "{ \"data\" : [${StringEscapeUtils.unescapeEcmaScript(jsonString)}"
|
||||
|
||||
val mapper = ObjectMapper()
|
||||
.disable(MapperFeature.AUTO_DETECT_SETTERS)
|
||||
.disable(MapperFeature.AUTO_DETECT_SETTERS)
|
||||
|
||||
return try {
|
||||
val data = mapper.readValue(jsonString, MenuData::class.java)
|
||||
@ -48,11 +63,14 @@ fun parseMenu(call: Call): MenuData? {
|
||||
// parse footer content
|
||||
|
||||
val footer = fullString.substringAfter("footerMarkup", "")
|
||||
.substringAfter("{", "")
|
||||
.substringBefore("}", "")
|
||||
.substringAfter("{", "")
|
||||
.substringBefore("}", "")
|
||||
|
||||
val doc = Jsoup.parseBodyFragment(StringEscapeUtils.unescapeEcmaScript(
|
||||
StringEscapeUtils.unescapeEcmaScript(footer)))
|
||||
val doc = Jsoup.parseBodyFragment(
|
||||
StringEscapeUtils.unescapeEcmaScript(
|
||||
StringEscapeUtils.unescapeEcmaScript(footer)
|
||||
)
|
||||
)
|
||||
val footerData = mutableListOf<MenuFooterItem>()
|
||||
val footerSmallData = mutableListOf<MenuFooterItem>()
|
||||
|
||||
@ -76,11 +94,14 @@ fun parseMenu(call: Call): MenuData? {
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class MenuData(val data: List<MenuHeader> = emptyList(),
|
||||
val footer: MenuFooter = MenuFooter()) {
|
||||
data class MenuData(
|
||||
val data: List<MenuHeader> = emptyList(),
|
||||
val footer: MenuFooter = MenuFooter()
|
||||
) {
|
||||
|
||||
@JsonCreator constructor(
|
||||
@JsonProperty("data") data: List<MenuHeader>?
|
||||
@JsonCreator
|
||||
constructor(
|
||||
@JsonProperty("data") data: List<MenuHeader>?
|
||||
) : this(data ?: emptyList(), MenuFooter())
|
||||
|
||||
fun flatMapValid(): List<MenuItemData> {
|
||||
@ -95,7 +116,6 @@ data class MenuData(val data: List<MenuHeader> = emptyList(),
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface MenuItemData {
|
||||
@ -103,17 +123,20 @@ interface MenuItemData {
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class MenuHeader(val id: String? = null,
|
||||
val header: String? = null,
|
||||
val visible: List<MenuItem> = emptyList(),
|
||||
val all: List<MenuItem> = emptyList()) : MenuItemData {
|
||||
data class MenuHeader(
|
||||
val id: String? = null,
|
||||
val header: String? = null,
|
||||
val visible: List<MenuItem> = emptyList(),
|
||||
val all: List<MenuItem> = emptyList()
|
||||
) : MenuItemData {
|
||||
|
||||
@JsonCreator constructor(
|
||||
@JsonProperty("id") id: String?,
|
||||
@JsonProperty("header") header: String?,
|
||||
@JsonProperty("visible") visible: List<MenuItem>?,
|
||||
@JsonProperty("all") all: List<MenuItem>?,
|
||||
@JsonProperty("fake") fake: Boolean?
|
||||
@JsonCreator
|
||||
constructor(
|
||||
@JsonProperty("id") id: String?,
|
||||
@JsonProperty("header") header: String?,
|
||||
@JsonProperty("visible") visible: List<MenuItem>?,
|
||||
@JsonProperty("all") all: List<MenuItem>?,
|
||||
@JsonProperty("fake") fake: Boolean?
|
||||
) : this(id, header, visible ?: emptyList(), all ?: emptyList())
|
||||
|
||||
override val isValid: Boolean
|
||||
@ -121,41 +144,49 @@ data class MenuHeader(val id: String? = null,
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class MenuItem(val id: String? = null,
|
||||
val name: String? = null,
|
||||
val pic: String? = null,
|
||||
val url: String? = null,
|
||||
val badge: String? = null,
|
||||
val countDetails: String? = null) : MenuItemData {
|
||||
data class MenuItem(
|
||||
val id: String? = null,
|
||||
val name: String? = null,
|
||||
val pic: String? = null,
|
||||
val url: String? = null,
|
||||
val badge: String? = null,
|
||||
val countDetails: String? = null
|
||||
) : MenuItemData {
|
||||
|
||||
@JsonCreator constructor(
|
||||
@JsonProperty("id") id: String?,
|
||||
@JsonProperty("name") name: String?,
|
||||
@JsonProperty("pic") pic: String?,
|
||||
@JsonProperty("url") url: String?,
|
||||
@JsonProperty("count") badge: String?,
|
||||
@JsonProperty("count_details") countDetails: String?,
|
||||
@JsonProperty("fake") fake: Boolean?
|
||||
) : this(id, name, pic?.formattedFbUrl,
|
||||
url?.formattedFbUrl,
|
||||
if (badge == "0") null else badge,
|
||||
countDetails)
|
||||
@JsonCreator
|
||||
constructor(
|
||||
@JsonProperty("id") id: String?,
|
||||
@JsonProperty("name") name: String?,
|
||||
@JsonProperty("pic") pic: String?,
|
||||
@JsonProperty("url") url: String?,
|
||||
@JsonProperty("count") badge: String?,
|
||||
@JsonProperty("count_details") countDetails: String?,
|
||||
@JsonProperty("fake") fake: Boolean?
|
||||
) : this(
|
||||
id, name, pic?.formattedFbUrl,
|
||||
url?.formattedFbUrl,
|
||||
if (badge == "0") null else badge,
|
||||
countDetails
|
||||
)
|
||||
|
||||
override val isValid: Boolean
|
||||
get() = !name.isNullOrBlank() && !url.isNullOrBlank()
|
||||
}
|
||||
|
||||
data class MenuFooter(val data: List<MenuFooterItem> = emptyList(),
|
||||
val smallData: List<MenuFooterItem> = emptyList()) {
|
||||
data class MenuFooter(
|
||||
val data: List<MenuFooterItem> = emptyList(),
|
||||
val smallData: List<MenuFooterItem> = emptyList()
|
||||
) {
|
||||
|
||||
val hasContent
|
||||
get() = data.isNotEmpty() || smallData.isNotEmpty()
|
||||
|
||||
}
|
||||
|
||||
data class MenuFooterItem(val name: String? = null,
|
||||
val url: String? = null,
|
||||
val isSmall: Boolean = false) : MenuItemData {
|
||||
data class MenuFooterItem(
|
||||
val name: String? = null,
|
||||
val url: String? = null,
|
||||
val isSmall: Boolean = false
|
||||
) : MenuItemData {
|
||||
override val isValid: Boolean
|
||||
get() = name != null && url != null
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.requests
|
||||
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
@ -10,10 +26,10 @@ fun RequestAuth.sendMessage(group: String, content: String): FrostRequest<Boolea
|
||||
|
||||
// todo test more; only tested against tids=cid...
|
||||
val body = listOf(
|
||||
"tids" to group,
|
||||
"body" to content,
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
"tids" to group,
|
||||
"body" to content,
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
|
||||
|
||||
return frostRequest(::validateMessage) {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.facebook.requests
|
||||
|
||||
import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
@ -8,11 +24,11 @@ import com.pitchedapps.frost.facebook.FB_URL_BASE
|
||||
fun RequestAuth.markNotificationRead(notifId: Long): FrostRequest<Boolean> {
|
||||
|
||||
val body = listOf(
|
||||
"click_type" to "notification_click",
|
||||
"id" to notifId,
|
||||
"target_id" to "null",
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
"click_type" to "notification_click",
|
||||
"id" to notifId,
|
||||
"target_id" to "null",
|
||||
"fb_dtsg" to fb_dtsg,
|
||||
"__user" to userId
|
||||
).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
|
||||
|
||||
return frostRequest(::executeForNoError) {
|
||||
@ -22,6 +38,6 @@ fun RequestAuth.markNotificationRead(notifId: Long): FrostRequest<Boolean> {
|
||||
}
|
||||
|
||||
fun RequestAuth.markNotificationsRead(vararg notifId: Long) =
|
||||
notifId.toTypedArray().zip<Long, Boolean, Boolean>(
|
||||
{ it.all { self -> self } },
|
||||
{ markNotificationRead(it).invoke() })
|
||||
notifId.toTypedArray().zip<Long, Boolean, Boolean>(
|
||||
{ it.all { self -> self } },
|
||||
{ markNotificationRead(it).invoke() })
|
||||
|
@ -1,15 +1,31 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import ca.allanwang.kau.utils.fadeScaleTransition
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.withArguments
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.pitchedapps.frost.contracts.DynamicUiContract
|
||||
import com.pitchedapps.frost.contracts.FrostContentParent
|
||||
@ -17,7 +33,12 @@ import com.pitchedapps.frost.contracts.MainActivityContract
|
||||
import com.pitchedapps.frost.contracts.MainFabContract
|
||||
import com.pitchedapps.frost.enums.FeedSort
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.ARG_URL
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.REQUEST_REFRESH
|
||||
import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM
|
||||
import com.pitchedapps.frost.utils.frostEvent
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
@ -33,12 +54,17 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
|
||||
private const val ARG_POSITION = "arg_position"
|
||||
private const val ARG_VALID = "arg_valid"
|
||||
|
||||
internal operator fun invoke(base: () -> BaseFragment, useFallback: Boolean, data: FbItem, position: Int): BaseFragment {
|
||||
internal operator fun invoke(
|
||||
base: () -> BaseFragment,
|
||||
useFallback: Boolean,
|
||||
data: FbItem,
|
||||
position: Int
|
||||
): BaseFragment {
|
||||
val fragment = if (!useFallback) base() else WebFragment()
|
||||
val d = if (data == FbItem.FEED) FeedSort(Prefs.feedSort).item else data
|
||||
fragment.withArguments(
|
||||
ARG_URL to d.url,
|
||||
ARG_POSITION to position
|
||||
ARG_URL to d.url,
|
||||
ARG_POSITION to position
|
||||
)
|
||||
d.put(fragment.arguments!!)
|
||||
return fragment
|
||||
@ -55,8 +81,10 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
|
||||
if (value || this is WebFragment) return
|
||||
arguments!!.putBoolean(ARG_VALID, value)
|
||||
L.e { "Invalidating position $position" }
|
||||
frostEvent("Native Fallback",
|
||||
"Item" to baseEnum.name)
|
||||
frostEvent(
|
||||
"Native Fallback",
|
||||
"Item" to baseEnum.name
|
||||
)
|
||||
(context as MainActivityContract).reloadFragment(this)
|
||||
}
|
||||
|
||||
@ -75,10 +103,14 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
|
||||
throw IllegalArgumentException("${this::class.java.simpleName} is not attached to a context implementing MainActivityContract")
|
||||
}
|
||||
|
||||
final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
final override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(layoutRes, container, false)
|
||||
val content = view as? FrostContentParent
|
||||
?: throw IllegalArgumentException("layoutRes for fragment must return view implementing FrostContentParent")
|
||||
val content = view as? FrostContentParent
|
||||
?: throw IllegalArgumentException("layoutRes for fragment must return view implementing FrostContentParent")
|
||||
this.content = content
|
||||
content.bind(this)
|
||||
return view
|
||||
@ -113,28 +145,28 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
|
||||
}
|
||||
|
||||
override fun attachMainObservable(contract: MainActivityContract): Disposable =
|
||||
contract.fragmentSubject.observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
when (it) {
|
||||
REQUEST_REFRESH -> {
|
||||
core?.apply {
|
||||
clearHistory()
|
||||
firstLoad = true
|
||||
firstLoadRequest()
|
||||
}
|
||||
}
|
||||
position -> {
|
||||
contract.setTitle(baseEnum.titleId)
|
||||
updateFab(contract)
|
||||
core?.active = true
|
||||
}
|
||||
-(position + 1) -> {
|
||||
core?.active = false
|
||||
}
|
||||
REQUEST_TEXT_ZOOM -> {
|
||||
reloadTextSize()
|
||||
contract.fragmentSubject.observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
when (it) {
|
||||
REQUEST_REFRESH -> {
|
||||
core?.apply {
|
||||
clearHistory()
|
||||
firstLoad = true
|
||||
firstLoadRequest()
|
||||
}
|
||||
}
|
||||
position -> {
|
||||
contract.setTitle(baseEnum.titleId)
|
||||
updateFab(contract)
|
||||
core?.active = true
|
||||
}
|
||||
-(position + 1) -> {
|
||||
core?.active = false
|
||||
}
|
||||
REQUEST_TEXT_ZOOM -> {
|
||||
reloadTextSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateFab(contract: MainFabContract) {
|
||||
contract.hideFab() // default
|
||||
@ -197,4 +229,3 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
|
||||
|
||||
override fun onTabClick(): Unit = content?.core?.onTabClicked() ?: Unit
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,26 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.fragments
|
||||
|
||||
import com.pitchedapps.frost.contracts.*
|
||||
import com.pitchedapps.frost.contracts.FrostContentContainer
|
||||
import com.pitchedapps.frost.contracts.FrostContentCore
|
||||
import com.pitchedapps.frost.contracts.FrostContentParent
|
||||
import com.pitchedapps.frost.contracts.MainActivityContract
|
||||
import com.pitchedapps.frost.contracts.MainFabContract
|
||||
import com.pitchedapps.frost.views.FrostRecyclerView
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
@ -74,8 +94,6 @@ interface FragmentContract : FrostContentContainer {
|
||||
fun onBackPressed(): Boolean
|
||||
|
||||
fun onTabClick()
|
||||
|
||||
|
||||
}
|
||||
|
||||
interface RecyclerContentContract {
|
||||
@ -88,5 +106,4 @@ interface RecyclerContentContract {
|
||||
* Callback returns [true] for success, [false] otherwise
|
||||
*/
|
||||
fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit)
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.fragments
|
||||
|
||||
import ca.allanwang.kau.adapters.fastAdapter
|
||||
@ -65,7 +81,6 @@ abstract class GenericRecyclerFragment<T, Item : IItem<*, *>> : RecyclerFragment
|
||||
* Create the fast adapter to bind to the recyclerview
|
||||
*/
|
||||
open fun getAdapter(): FastAdapter<IItem<*, *>> = fastAdapter(this.adapter)
|
||||
|
||||
}
|
||||
|
||||
abstract class FrostParserFragment<T : Any, Item : IItem<*, *>> : RecyclerFragment() {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.fragments
|
||||
|
||||
import com.mikepenz.fastadapter.IItem
|
||||
@ -6,8 +22,18 @@ import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.facebook.parsers.FrostNotifs
|
||||
import com.pitchedapps.frost.facebook.parsers.NotifParser
|
||||
import com.pitchedapps.frost.facebook.parsers.ParseResponse
|
||||
import com.pitchedapps.frost.facebook.requests.*
|
||||
import com.pitchedapps.frost.iitems.*
|
||||
import com.pitchedapps.frost.facebook.requests.MenuFooterItem
|
||||
import com.pitchedapps.frost.facebook.requests.MenuHeader
|
||||
import com.pitchedapps.frost.facebook.requests.MenuItem
|
||||
import com.pitchedapps.frost.facebook.requests.MenuItemData
|
||||
import com.pitchedapps.frost.facebook.requests.fbRequest
|
||||
import com.pitchedapps.frost.facebook.requests.getMenuData
|
||||
import com.pitchedapps.frost.iitems.ClickableIItemContract
|
||||
import com.pitchedapps.frost.iitems.MenuContentIItem
|
||||
import com.pitchedapps.frost.iitems.MenuFooterIItem
|
||||
import com.pitchedapps.frost.iitems.MenuFooterSmallIItem
|
||||
import com.pitchedapps.frost.iitems.MenuHeaderIItem
|
||||
import com.pitchedapps.frost.iitems.NotificationIItem
|
||||
import com.pitchedapps.frost.utils.frostJsoup
|
||||
import com.pitchedapps.frost.views.FrostRecyclerView
|
||||
import org.jetbrains.anko.doAsync
|
||||
@ -23,7 +49,7 @@ class NotificationFragment : FrostParserFragment<FrostNotifs, NotificationIItem>
|
||||
override fun getDoc(cookie: String?) = frostJsoup(cookie, "${FbItem.NOTIFICATIONS.url}?more")
|
||||
|
||||
override fun toItems(response: ParseResponse<FrostNotifs>): List<NotificationIItem> =
|
||||
response.data.notifs.map { NotificationIItem(it, response.cookie) }
|
||||
response.data.notifs.map { NotificationIItem(it, response.cookie) }
|
||||
|
||||
override fun bindImpl(recyclerView: FrostRecyclerView) {
|
||||
NotificationIItem.bindEvents(adapter)
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.fragments
|
||||
|
||||
import android.webkit.WebView
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.glide
|
||||
|
||||
import android.content.Context
|
||||
@ -30,11 +46,11 @@ object FrostGlide {
|
||||
}
|
||||
|
||||
fun <T> RequestBuilder<T>.transform(vararg transformation: BitmapTransformation): RequestBuilder<T> =
|
||||
when (transformation.size) {
|
||||
0 -> this
|
||||
1 -> apply(RequestOptions.bitmapTransform(transformation[0]))
|
||||
else -> apply(RequestOptions.bitmapTransform(MultiTransformation(*transformation)))
|
||||
}
|
||||
when (transformation.size) {
|
||||
0 -> this
|
||||
1 -> apply(RequestOptions.bitmapTransform(transformation[0]))
|
||||
else -> apply(RequestOptions.bitmapTransform(MultiTransformation(*transformation)))
|
||||
}
|
||||
|
||||
@GlideModule
|
||||
class FrostGlideModule : AppGlideModule() {
|
||||
@ -48,7 +64,7 @@ class FrostGlideModule : AppGlideModule() {
|
||||
}
|
||||
|
||||
private fun getFrostHttpClient(): OkHttpClient =
|
||||
OkHttpClient.Builder().addInterceptor(FrostCookieInterceptor()).build()
|
||||
OkHttpClient.Builder().addInterceptor(FrostCookieInterceptor()).build()
|
||||
|
||||
class FrostCookieInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
|
@ -1,12 +1,32 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.glide
|
||||
|
||||
import android.graphics.*
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapShader
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Shader
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import java.security.MessageDigest
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 27/12/17.
|
||||
*/
|
||||
@ -25,17 +45,17 @@ class RoundCornerTransformation : BitmapTransformation() {
|
||||
bitmap.setHasAlpha(true)
|
||||
|
||||
val radius = Math.min(width, height).toFloat() /
|
||||
(if (Prefs.showRoundedIcons) 2f else 10f)
|
||||
(if (Prefs.showRoundedIcons) 2f else 10f)
|
||||
|
||||
val canvas = Canvas(bitmap)
|
||||
val paint = Paint()
|
||||
paint.isAntiAlias = true
|
||||
paint.shader = BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
|
||||
canvas.drawRoundRect(RectF(0f, 0f, width.toFloat(), height.toFloat()),
|
||||
radius, radius, paint)
|
||||
canvas.drawRoundRect(
|
||||
RectF(0f, 0f, width.toFloat(), height.toFloat()),
|
||||
radius, radius, paint
|
||||
)
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.iitems
|
||||
|
||||
import android.content.Context
|
||||
@ -32,25 +48,25 @@ interface ClickableIItemContract {
|
||||
companion object {
|
||||
fun bindEvents(adapter: IAdapter<IItem<*, *>>) {
|
||||
adapter.fastAdapter.withSelectable(false)
|
||||
.withOnClickListener { v, _, item, _ ->
|
||||
if (item is ClickableIItemContract) {
|
||||
item.click(v!!.context)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
.withOnClickListener { v, _, item, _ ->
|
||||
if (item is ClickableIItemContract) {
|
||||
item.click(v!!.context)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic header item
|
||||
* Not clickable with an accent color
|
||||
*/
|
||||
open class HeaderIItem(val text: String?,
|
||||
itemId: Int = R.layout.iitem_header)
|
||||
: KauIItem<HeaderIItem, HeaderIItem.ViewHolder>(R.layout.iitem_header, ::ViewHolder, itemId) {
|
||||
open class HeaderIItem(
|
||||
val text: String?,
|
||||
itemId: Int = R.layout.iitem_header
|
||||
) : KauIItem<HeaderIItem, HeaderIItem.ViewHolder>(R.layout.iitem_header, ::ViewHolder, itemId) {
|
||||
|
||||
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<HeaderIItem>(itemView) {
|
||||
|
||||
@ -66,18 +82,18 @@ open class HeaderIItem(val text: String?,
|
||||
text.text = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic text item
|
||||
* Clickable with text color
|
||||
*/
|
||||
open class TextIItem(val text: String?,
|
||||
override val url: String?,
|
||||
itemId: Int = R.layout.iitem_text)
|
||||
: KauIItem<TextIItem, TextIItem.ViewHolder>(R.layout.iitem_text, ::ViewHolder, itemId),
|
||||
ClickableIItemContract {
|
||||
open class TextIItem(
|
||||
val text: String?,
|
||||
override val url: String?,
|
||||
itemId: Int = R.layout.iitem_text
|
||||
) : KauIItem<TextIItem, TextIItem.ViewHolder>(R.layout.iitem_text, ::ViewHolder, itemId),
|
||||
ClickableIItemContract {
|
||||
|
||||
class ViewHolder(itemView: View) : FastAdapter.ViewHolder<TextIItem>(itemView) {
|
||||
|
||||
@ -93,5 +109,4 @@ open class TextIItem(val text: String?,
|
||||
text.text = null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.iitems
|
||||
|
||||
import android.view.View
|
||||
@ -21,9 +37,9 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
/**
|
||||
* Created by Allan Wang on 30/12/17.
|
||||
*/
|
||||
class MenuContentIItem(val data: MenuItem)
|
||||
: KauIItem<MenuContentIItem, MenuContentIItem.ViewHolder>(R.layout.iitem_menu, ::ViewHolder),
|
||||
ClickableIItemContract {
|
||||
class MenuContentIItem(val data: MenuItem) :
|
||||
KauIItem<MenuContentIItem, MenuContentIItem.ViewHolder>(R.layout.iitem_menu, ::ViewHolder),
|
||||
ClickableIItemContract {
|
||||
|
||||
override val url: String?
|
||||
get() = data.url
|
||||
@ -42,9 +58,9 @@ class MenuContentIItem(val data: MenuItem)
|
||||
val iconUrl = item.data.pic
|
||||
if (iconUrl != null)
|
||||
GlideApp.with(itemView)
|
||||
.load(iconUrl)
|
||||
.transform(FrostGlide.roundCorner)
|
||||
.into(icon.visible())
|
||||
.load(iconUrl)
|
||||
.transform(FrostGlide.roundCorner)
|
||||
.into(icon.visible())
|
||||
else
|
||||
icon.gone()
|
||||
content.text = item.data.name
|
||||
@ -59,12 +75,11 @@ class MenuContentIItem(val data: MenuItem)
|
||||
}
|
||||
}
|
||||
|
||||
class MenuHeaderIItem(val data: MenuHeader) : HeaderIItem(data.header,
|
||||
itemId = R.id.item_menu_header)
|
||||
class MenuHeaderIItem(val data: MenuHeader) : HeaderIItem(
|
||||
data.header,
|
||||
itemId = R.id.item_menu_header
|
||||
)
|
||||
|
||||
class MenuFooterIItem(val data: MenuFooterItem)
|
||||
: TextIItem(data.name, data.url, R.id.item_menu_footer)
|
||||
|
||||
class MenuFooterSmallIItem(val data: MenuFooterItem)
|
||||
: TextIItem(data.name, data.url, R.id.item_menu_footer_small)
|
||||
class MenuFooterIItem(val data: MenuFooterItem) : TextIItem(data.name, data.url, R.id.item_menu_footer)
|
||||
|
||||
class MenuFooterSmallIItem(val data: MenuFooterItem) : TextIItem(data.name, data.url, R.id.item_menu_footer_small)
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.iitems
|
||||
|
||||
import android.view.View
|
||||
@ -26,23 +42,24 @@ import com.pitchedapps.frost.utils.launchWebOverlay
|
||||
/**
|
||||
* Created by Allan Wang on 27/12/17.
|
||||
*/
|
||||
class NotificationIItem(val notification: FrostNotif, val cookie: String) : KauIItem<NotificationIItem, NotificationIItem.ViewHolder>(
|
||||
class NotificationIItem(val notification: FrostNotif, val cookie: String) :
|
||||
KauIItem<NotificationIItem, NotificationIItem.ViewHolder>(
|
||||
R.layout.iitem_notification, ::ViewHolder
|
||||
) {
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun bindEvents(adapter: ItemAdapter<NotificationIItem>) {
|
||||
adapter.fastAdapter.withSelectable(false)
|
||||
.withOnClickListener { v, _, item, position ->
|
||||
val notif = item.notification
|
||||
if (notif.unread) {
|
||||
FrostRunnable.markNotificationRead(v!!.context, notif.id, item.cookie)
|
||||
adapter.set(position, NotificationIItem(notif.copy(unread = false), item.cookie))
|
||||
}
|
||||
// TODO temp fix. If url is dependent, we cannot load it directly
|
||||
v!!.context.launchWebOverlay(if (notif.url.isIndependent) notif.url else FbItem.NOTIFICATIONS.url)
|
||||
true
|
||||
.withOnClickListener { v, _, item, position ->
|
||||
val notif = item.notification
|
||||
if (notif.unread) {
|
||||
FrostRunnable.markNotificationRead(v!!.context, notif.id, item.cookie)
|
||||
adapter.set(position, NotificationIItem(notif.copy(unread = false), item.cookie))
|
||||
}
|
||||
// TODO temp fix. If url is dependent, we cannot load it directly
|
||||
v!!.context.launchWebOverlay(if (notif.url.isIndependent) notif.url else FbItem.NOTIFICATIONS.url)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
//todo see if necessary
|
||||
@ -52,12 +69,17 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : KauI
|
||||
private class Diff : DiffCallback<NotificationIItem> {
|
||||
|
||||
override fun areItemsTheSame(oldItem: NotificationIItem, newItem: NotificationIItem) =
|
||||
oldItem.notification.id == newItem.notification.id
|
||||
oldItem.notification.id == newItem.notification.id
|
||||
|
||||
override fun areContentsTheSame(oldItem: NotificationIItem, newItem: NotificationIItem) =
|
||||
oldItem.notification == newItem.notification
|
||||
oldItem.notification == newItem.notification
|
||||
|
||||
override fun getChangePayload(oldItem: NotificationIItem, oldItemPosition: Int, newItem: NotificationIItem, newItemPosition: Int): Any? {
|
||||
override fun getChangePayload(
|
||||
oldItem: NotificationIItem,
|
||||
oldItemPosition: Int,
|
||||
newItem: NotificationIItem,
|
||||
newItemPosition: Int
|
||||
): Any? {
|
||||
return newItem
|
||||
}
|
||||
}
|
||||
@ -75,15 +97,17 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : KauI
|
||||
|
||||
override fun bindView(item: NotificationIItem, payloads: MutableList<Any>) {
|
||||
val notif = item.notification
|
||||
frame.background = createSimpleRippleDrawable(Prefs.textColor,
|
||||
Prefs.nativeBgColor(notif.unread))
|
||||
frame.background = createSimpleRippleDrawable(
|
||||
Prefs.textColor,
|
||||
Prefs.nativeBgColor(notif.unread)
|
||||
)
|
||||
content.setTextColor(Prefs.textColor)
|
||||
date.setTextColor(Prefs.textColor.withAlpha(150))
|
||||
|
||||
val glide = glide
|
||||
glide.load(notif.img)
|
||||
.transform(FrostGlide.roundCorner)
|
||||
.into(avatar)
|
||||
.transform(FrostGlide.roundCorner)
|
||||
.into(avatar)
|
||||
if (notif.thumbnailUrl != null)
|
||||
glide.load(notif.thumbnailUrl).into(thumbnail.visible())
|
||||
|
||||
|
@ -1,10 +1,30 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.iitems
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import ca.allanwang.kau.iitems.KauIItem
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.invisible
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.visible
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.IItem
|
||||
import com.mikepenz.fastadapter_extensions.drag.IDraggable
|
||||
@ -16,8 +36,8 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
* Created by Allan Wang on 26/11/17.
|
||||
*/
|
||||
class TabIItem(val item: FbItem) : KauIItem<TabIItem, TabIItem.ViewHolder>(
|
||||
R.layout.iitem_tab_preview,
|
||||
{ ViewHolder(it) }
|
||||
R.layout.iitem_tab_preview,
|
||||
{ ViewHolder(it) }
|
||||
), IDraggable<TabIItem, IItem<*, *>> {
|
||||
|
||||
override fun withIsDraggable(draggable: Boolean): TabIItem = this
|
||||
@ -45,6 +65,5 @@ class TabIItem(val item: FbItem) : KauIItem<TabIItem, TabIItem.ViewHolder>(
|
||||
image.setImageDrawable(null)
|
||||
text.visible().text = null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,14 +1,35 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.injectors
|
||||
|
||||
import android.graphics.Color
|
||||
import android.webkit.WebView
|
||||
import ca.allanwang.kau.kotlin.lazyContext
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.adjustAlpha
|
||||
import ca.allanwang.kau.utils.colorToBackground
|
||||
import ca.allanwang.kau.utils.colorToForeground
|
||||
import ca.allanwang.kau.utils.toRgbaString
|
||||
import ca.allanwang.kau.utils.use
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-31.
|
||||
@ -32,17 +53,17 @@ enum class CssAssets(val folder: String = "themes") : InjectorContract {
|
||||
val bb = Prefs.bgColor.colorToForeground(0.35f)
|
||||
|
||||
content = content
|
||||
.replace("\$T\$", Prefs.textColor.toRgbaString())
|
||||
.replace("\$TT\$", Prefs.textColor.colorToBackground(0.05f).toRgbaString())
|
||||
.replace("\$A\$", Prefs.accentColor.toRgbaString())
|
||||
.replace("\$B\$", Prefs.bgColor.toRgbaString())
|
||||
.replace("\$BT\$", bt)
|
||||
.replace("\$BBT\$", bb.withAlpha(51).toRgbaString())
|
||||
.replace("\$O\$", Prefs.bgColor.withAlpha(255).toRgbaString())
|
||||
.replace("\$OO\$", bb.withAlpha(255).toRgbaString())
|
||||
.replace("\$D\$", Prefs.textColor.adjustAlpha(0.3f).toRgbaString())
|
||||
.replace("\$TI\$", bb.withAlpha(60).toRgbaString())
|
||||
.replace("\$C\$", bt)
|
||||
.replace("\$T\$", Prefs.textColor.toRgbaString())
|
||||
.replace("\$TT\$", Prefs.textColor.colorToBackground(0.05f).toRgbaString())
|
||||
.replace("\$A\$", Prefs.accentColor.toRgbaString())
|
||||
.replace("\$B\$", Prefs.bgColor.toRgbaString())
|
||||
.replace("\$BT\$", bt)
|
||||
.replace("\$BBT\$", bb.withAlpha(51).toRgbaString())
|
||||
.replace("\$O\$", Prefs.bgColor.withAlpha(255).toRgbaString())
|
||||
.replace("\$OO\$", bb.withAlpha(255).toRgbaString())
|
||||
.replace("\$D\$", Prefs.textColor.adjustAlpha(0.3f).toRgbaString())
|
||||
.replace("\$TI\$", bb.withAlpha(60).toRgbaString())
|
||||
.replace("\$C\$", bt)
|
||||
}
|
||||
JsBuilder().css(content).build()
|
||||
} catch (e: FileNotFoundException) {
|
||||
@ -58,5 +79,4 @@ enum class CssAssets(val folder: String = "themes") : InjectorContract {
|
||||
fun reset() {
|
||||
injector.invalidate()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.injectors
|
||||
|
||||
import android.webkit.WebView
|
||||
@ -9,10 +25,14 @@ import android.webkit.WebView
|
||||
*/
|
||||
enum class CssHider(vararg val items: String) : InjectorContract {
|
||||
CORE("[data-sigil=m_login_upsell]", "[role=progressbar]"),
|
||||
HEADER("#header", "#mJewelNav", "[data-sigil=MTopBlueBarHeader]",
|
||||
"#header-notices", "[data-sigil*=m-promo-jewel-header]"),
|
||||
ADS("article[data-xt*=sponsor]",
|
||||
"article[data-store*=sponsor]"),
|
||||
HEADER(
|
||||
"#header", "#mJewelNav", "[data-sigil=MTopBlueBarHeader]",
|
||||
"#header-notices", "[data-sigil*=m-promo-jewel-header]"
|
||||
),
|
||||
ADS(
|
||||
"article[data-xt*=sponsor]",
|
||||
"article[data-store*=sponsor]"
|
||||
),
|
||||
PEOPLE_YOU_MAY_KNOW("article._d2r"),
|
||||
SUGGESTED_GROUPS("article[data-ft*=\"ei\":]"),
|
||||
COMPOSER("#MComposer"),
|
||||
@ -22,11 +42,10 @@ enum class CssHider(vararg val items: String) : InjectorContract {
|
||||
|
||||
val injector: JsInjector by lazy {
|
||||
JsBuilder().css("${items.joinToString(separator = ",")}{display:none !important}")
|
||||
.single(name).build()
|
||||
.single(name).build()
|
||||
}
|
||||
|
||||
override fun inject(webView: WebView, callback: (() -> Unit)?) {
|
||||
injector.inject(webView, callback)
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.injectors
|
||||
|
||||
import android.webkit.WebView
|
||||
@ -27,10 +43,9 @@ enum class JsActions(body: String) : InjectorContract {
|
||||
val function = "(function(){$body})();"
|
||||
|
||||
override fun inject(webView: WebView, callback: (() -> Unit)?) =
|
||||
JsInjector(function).inject(webView, callback)
|
||||
|
||||
JsInjector(function).inject(webView, callback)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun clickBySelector(selector: String): String =
|
||||
"""document.querySelector("$selector").click()"""
|
||||
"""document.querySelector("$selector").click()"""
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.injectors
|
||||
|
||||
import android.webkit.WebView
|
||||
@ -5,7 +21,7 @@ import ca.allanwang.kau.kotlin.lazyContext
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-31.
|
||||
@ -31,5 +47,4 @@ enum class JsAssets : InjectorContract {
|
||||
override fun inject(webView: WebView, callback: (() -> Unit)?) {
|
||||
injector(webView.context).inject(webView, callback)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.injectors
|
||||
|
||||
import android.webkit.WebView
|
||||
@ -8,7 +24,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.subjects.SingleSubject
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
|
||||
class JsBuilder {
|
||||
private val css = StringBuilder()
|
||||
@ -90,10 +106,10 @@ fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Int) -> Uni
|
||||
}
|
||||
val observables = Array(validInjectors.size) { SingleSubject.create<Unit>() }
|
||||
val disposable = Single.zip<Unit, Int>(observables.asList()) { it.size }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { res, _ ->
|
||||
callback(res)
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { res, _ ->
|
||||
callback(res)
|
||||
}
|
||||
(0 until validInjectors.size).forEach { i ->
|
||||
validInjectors[i].inject(this) {
|
||||
observables[i].onSuccess(Unit)
|
||||
@ -102,8 +118,10 @@ fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Int) -> Uni
|
||||
return disposable
|
||||
}
|
||||
|
||||
fun FrostWebViewClient.jsInject(vararg injectors: InjectorContract,
|
||||
callback: ((Int) -> Unit)? = null) = web.jsInject(*injectors, callback = callback)
|
||||
fun FrostWebViewClient.jsInject(
|
||||
vararg injectors: InjectorContract,
|
||||
callback: ((Int) -> Unit)? = null
|
||||
) = web.jsInject(*injectors, callback = callback)
|
||||
|
||||
/**
|
||||
* Wrapper class to convert a function into an injector
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.intro
|
||||
|
||||
import android.os.Bundle
|
||||
@ -17,7 +33,11 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) {
|
||||
val themeList
|
||||
get() = listOf(intro_theme_light, intro_theme_dark, intro_theme_amoled, intro_theme_glass)
|
||||
|
||||
override fun viewArray(): Array<Array<out View>> = arrayOf(arrayOf(title), arrayOf(intro_theme_light, intro_theme_dark), arrayOf(intro_theme_amoled, intro_theme_glass))
|
||||
override fun viewArray(): Array<Array<out View>> = arrayOf(
|
||||
arrayOf(title),
|
||||
arrayOf(intro_theme_light, intro_theme_dark),
|
||||
arrayOf(intro_theme_amoled, intro_theme_glass)
|
||||
)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
@ -40,5 +60,4 @@ class IntroFragmentTheme : BaseIntroFragment(R.layout.intro_theme) {
|
||||
themeList.forEach { it.animate().scaleXY(if (it == this) 1.6f else 0.8f).start() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.intro
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
@ -5,7 +21,12 @@ import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindViewResettable
|
||||
import ca.allanwang.kau.utils.colorToForeground
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.visible
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
@ -15,9 +36,9 @@ import com.pitchedapps.frost.utils.launchTabCustomizerActivity
|
||||
* Created by Allan Wang on 2017-07-28.
|
||||
*/
|
||||
abstract class BaseImageIntroFragment(
|
||||
val titleRes: Int,
|
||||
val imageRes: Int,
|
||||
val descRes: Int
|
||||
val titleRes: Int,
|
||||
val imageRes: Int,
|
||||
val descRes: Int
|
||||
) : BaseIntroFragment(R.layout.intro_image) {
|
||||
|
||||
val imageDrawable: LayerDrawable by lazyResettableRegistered { image.drawable as LayerDrawable }
|
||||
@ -68,7 +89,7 @@ abstract class BaseImageIntroFragment(
|
||||
}
|
||||
|
||||
class IntroAccountFragment : BaseImageIntroFragment(
|
||||
R.string.intro_multiple_accounts, R.drawable.intro_phone_nav, R.string.intro_multiple_accounts_desc
|
||||
R.string.intro_multiple_accounts, R.drawable.intro_phone_nav, R.string.intro_multiple_accounts_desc
|
||||
) {
|
||||
|
||||
override fun themeFragmentImpl() {
|
||||
@ -85,7 +106,7 @@ class IntroAccountFragment : BaseImageIntroFragment(
|
||||
}
|
||||
|
||||
class IntroTabTouchFragment : BaseImageIntroFragment(
|
||||
R.string.intro_easy_navigation, R.drawable.intro_phone_tab, R.string.intro_easy_navigation_desc
|
||||
R.string.intro_easy_navigation, R.drawable.intro_phone_tab, R.string.intro_easy_navigation_desc
|
||||
) {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -98,14 +119,20 @@ class IntroTabTouchFragment : BaseImageIntroFragment(
|
||||
|
||||
override fun themeFragmentImpl() {
|
||||
super.themeFragmentImpl()
|
||||
themeImageComponent(Prefs.iconColor, R.id.intro_phone_icon_1, R.id.intro_phone_icon_2, R.id.intro_phone_icon_3, R.id.intro_phone_icon_4)
|
||||
themeImageComponent(
|
||||
Prefs.iconColor,
|
||||
R.id.intro_phone_icon_1,
|
||||
R.id.intro_phone_icon_2,
|
||||
R.id.intro_phone_icon_3,
|
||||
R.id.intro_phone_icon_4
|
||||
)
|
||||
themeImageComponent(Prefs.headerColor, R.id.intro_phone_tab)
|
||||
themeImageComponent(Prefs.textColor.withAlpha(80), R.id.intro_phone_icon_ripple)
|
||||
}
|
||||
}
|
||||
|
||||
class IntroTabContextFragment : BaseImageIntroFragment(
|
||||
R.string.intro_context_aware, R.drawable.intro_phone_long_press, R.string.intro_context_aware_desc
|
||||
R.string.intro_context_aware, R.drawable.intro_phone_long_press, R.string.intro_context_aware_desc
|
||||
) {
|
||||
|
||||
override fun themeFragmentImpl() {
|
||||
@ -115,7 +142,12 @@ class IntroTabContextFragment : BaseImageIntroFragment(
|
||||
themeImageComponent(Prefs.bgColor.colorToForeground(0.2f), R.id.intro_phone_like, R.id.intro_phone_share)
|
||||
themeImageComponent(Prefs.bgColor.colorToForeground(0.3f), R.id.intro_phone_comment)
|
||||
themeImageComponent(Prefs.bgColor.colorToForeground(0.1f), R.id.intro_phone_card_1, R.id.intro_phone_card_2)
|
||||
themeImageComponent(Prefs.textColor, R.id.intro_phone_image_indicator, R.id.intro_phone_comment_indicator, R.id.intro_phone_card_indicator)
|
||||
themeImageComponent(
|
||||
Prefs.textColor,
|
||||
R.id.intro_phone_image_indicator,
|
||||
R.id.intro_phone_comment_indicator,
|
||||
R.id.intro_phone_card_indicator
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageScrolledImpl(positionOffset: Float) {
|
||||
|
@ -1,15 +1,31 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.intro
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import ca.allanwang.kau.kotlin.LazyResettableRegistry
|
||||
import ca.allanwang.kau.utils.Kotterknife
|
||||
import ca.allanwang.kau.utils.bindViewResettable
|
||||
@ -99,7 +115,6 @@ abstract class BaseIntroFragment(val layoutRes: Int) : Fragment() {
|
||||
}
|
||||
|
||||
protected open fun onPageSelectedImpl() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.rx
|
||||
|
||||
import io.reactivex.Single
|
||||
@ -72,9 +88,9 @@ abstract class RxFlyweight<in T : Any, C : Any, R : Any> {
|
||||
* you likely won't have a need for flyweights
|
||||
*/
|
||||
protected open fun createNewSource(input: T): Single<R> =
|
||||
Single.fromCallable { call(input) }
|
||||
.timeout(15, TimeUnit.SECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
Single.fromCallable { call(input) }
|
||||
.timeout(15, TimeUnit.SECONDS)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
fun reset() {
|
||||
synchronized(lock) {
|
||||
@ -82,5 +98,4 @@ abstract class RxFlyweight<in T : Any, C : Any, R : Any> {
|
||||
conditionals.clear()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.app.Notification
|
||||
@ -26,8 +42,12 @@ import com.pitchedapps.frost.facebook.parsers.NotifParser
|
||||
import com.pitchedapps.frost.facebook.parsers.ParseNotification
|
||||
import com.pitchedapps.frost.glide.FrostGlide
|
||||
import com.pitchedapps.frost.glide.GlideApp
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import java.util.*
|
||||
import com.pitchedapps.frost.utils.ARG_USER_ID
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.frostEvent
|
||||
import com.pitchedapps.frost.utils.isIndependent
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-07-08.
|
||||
@ -40,33 +60,38 @@ private val _40_DP = 40.dpToPx
|
||||
* Enum to handle notification creations
|
||||
*/
|
||||
enum class NotificationType(
|
||||
private val channelId: String,
|
||||
private val overlayContext: OverlayContext,
|
||||
private val fbItem: FbItem,
|
||||
private val parser: FrostParser<ParseNotification>,
|
||||
private val getTime: (notif: NotificationModel) -> Long,
|
||||
private val putTime: (notif: NotificationModel, time: Long) -> NotificationModel,
|
||||
private val ringtone: () -> String) {
|
||||
private val channelId: String,
|
||||
private val overlayContext: OverlayContext,
|
||||
private val fbItem: FbItem,
|
||||
private val parser: FrostParser<ParseNotification>,
|
||||
private val getTime: (notif: NotificationModel) -> Long,
|
||||
private val putTime: (notif: NotificationModel, time: Long) -> NotificationModel,
|
||||
private val ringtone: () -> String
|
||||
) {
|
||||
|
||||
GENERAL(NOTIF_CHANNEL_GENERAL,
|
||||
OverlayContext.NOTIFICATION,
|
||||
FbItem.NOTIFICATIONS,
|
||||
NotifParser,
|
||||
NotificationModel::epoch,
|
||||
{ notif, time -> notif.copy(epoch = time) },
|
||||
Prefs::notificationRingtone) {
|
||||
GENERAL(
|
||||
NOTIF_CHANNEL_GENERAL,
|
||||
OverlayContext.NOTIFICATION,
|
||||
FbItem.NOTIFICATIONS,
|
||||
NotifParser,
|
||||
NotificationModel::epoch,
|
||||
{ notif, time -> notif.copy(epoch = time) },
|
||||
Prefs::notificationRingtone
|
||||
) {
|
||||
|
||||
override fun bindRequest(content: NotificationContent, cookie: String) =
|
||||
FrostRunnable.prepareMarkNotificationRead(content.id, cookie)
|
||||
FrostRunnable.prepareMarkNotificationRead(content.id, cookie)
|
||||
},
|
||||
|
||||
MESSAGE(NOTIF_CHANNEL_MESSAGES,
|
||||
OverlayContext.MESSAGE,
|
||||
FbItem.MESSAGES,
|
||||
MessageParser,
|
||||
NotificationModel::epochIm,
|
||||
{ notif, time -> notif.copy(epochIm = time) },
|
||||
Prefs::messageRingtone);
|
||||
MESSAGE(
|
||||
NOTIF_CHANNEL_MESSAGES,
|
||||
OverlayContext.MESSAGE,
|
||||
FbItem.MESSAGES,
|
||||
MessageParser,
|
||||
NotificationModel::epochIm,
|
||||
{ notif, time -> notif.copy(epochIm = time) },
|
||||
Prefs::messageRingtone
|
||||
);
|
||||
|
||||
private val groupPrefix = "frost_${name.toLowerCase(Locale.CANADA)}"
|
||||
|
||||
@ -133,13 +158,15 @@ enum class NotificationType(
|
||||
}
|
||||
|
||||
fun debugNotification(context: Context, data: CookieModel) {
|
||||
val content = NotificationContent(data,
|
||||
System.currentTimeMillis(),
|
||||
"https://github.com/AllanWang/Frost-for-Facebook",
|
||||
"Debug Notif",
|
||||
"Test 123",
|
||||
System.currentTimeMillis() / 1000,
|
||||
"https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png")
|
||||
val content = NotificationContent(
|
||||
data,
|
||||
System.currentTimeMillis(),
|
||||
"https://github.com/AllanWang/Frost-for-Facebook",
|
||||
"Debug Notif",
|
||||
"Test 123",
|
||||
System.currentTimeMillis() / 1000,
|
||||
"https://www.iconexperience.com/_img/v_collection_png/256x256/shadow/dog.png"
|
||||
)
|
||||
createNotification(context, content).notify(context)
|
||||
}
|
||||
|
||||
@ -147,44 +174,43 @@ enum class NotificationType(
|
||||
* Create and submit a new notification with the given [content]
|
||||
*/
|
||||
private fun createNotification(context: Context, content: NotificationContent): FrostNotification =
|
||||
with(content) {
|
||||
val intent = Intent(context, FrostWebActivity::class.java)
|
||||
// TODO temp fix; we will show notification page for dependent urls. We can trigger a click next time
|
||||
intent.data = Uri.parse(if (href.isIndependent) href else FbItem.NOTIFICATIONS.url)
|
||||
intent.putExtra(ARG_USER_ID, data.id)
|
||||
overlayContext.put(intent)
|
||||
bindRequest(intent, content, data.cookie)
|
||||
with(content) {
|
||||
val intent = Intent(context, FrostWebActivity::class.java)
|
||||
// TODO temp fix; we will show notification page for dependent urls. We can trigger a click next time
|
||||
intent.data = Uri.parse(if (href.isIndependent) href else FbItem.NOTIFICATIONS.url)
|
||||
intent.putExtra(ARG_USER_ID, data.id)
|
||||
overlayContext.put(intent)
|
||||
bindRequest(intent, content, data.cookie)
|
||||
|
||||
val group = "${groupPrefix}_${data.id}"
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val notifBuilder = context.frostNotification(channelId)
|
||||
.setContentTitle(title ?: context.string(R.string.frost_name))
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setSubText(data.name)
|
||||
.setGroup(group)
|
||||
val group = "${groupPrefix}_${data.id}"
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val notifBuilder = context.frostNotification(channelId)
|
||||
.setContentTitle(title ?: context.string(R.string.frost_name))
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setSubText(data.name)
|
||||
.setGroup(group)
|
||||
|
||||
if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)
|
||||
L.v { "Notif load $content" }
|
||||
if (timestamp != -1L) notifBuilder.setWhen(timestamp * 1000)
|
||||
L.v { "Notif load $content" }
|
||||
|
||||
if (profileUrl != null) {
|
||||
try {
|
||||
val profileImg = GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(profileUrl)
|
||||
.transform(FrostGlide.circleCrop)
|
||||
.submit(_40_DP, _40_DP)
|
||||
.get()
|
||||
notifBuilder.setLargeIcon(profileImg)
|
||||
} catch (e: Exception) {
|
||||
L.e { "Failed to get image $profileUrl" }
|
||||
}
|
||||
if (profileUrl != null) {
|
||||
try {
|
||||
val profileImg = GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(profileUrl)
|
||||
.transform(FrostGlide.circleCrop)
|
||||
.submit(_40_DP, _40_DP)
|
||||
.get()
|
||||
notifBuilder.setLargeIcon(profileImg)
|
||||
} catch (e: Exception) {
|
||||
L.e { "Failed to get image $profileUrl" }
|
||||
}
|
||||
|
||||
FrostNotification(group, notifId, notifBuilder)
|
||||
}
|
||||
|
||||
FrostNotification(group, notifId, notifBuilder)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a summary notification to wrap the previous ones
|
||||
@ -198,12 +224,12 @@ enum class NotificationType(
|
||||
val group = "${groupPrefix}_$userId"
|
||||
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val notifBuilder = context.frostNotification(channelId)
|
||||
.setContentTitle(context.string(R.string.frost_name))
|
||||
.setContentText("$count ${context.string(fbItem.titleId)}")
|
||||
.setGroup(group)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setContentTitle(context.string(R.string.frost_name))
|
||||
.setContentText("$count ${context.string(fbItem.titleId)}")
|
||||
.setGroup(group)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notifBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
@ -211,31 +237,33 @@ enum class NotificationType(
|
||||
|
||||
return FrostNotification(group, 1, notifBuilder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification data holder
|
||||
*/
|
||||
data class NotificationContent(val data: CookieModel,
|
||||
val id: Long,
|
||||
val href: String,
|
||||
val title: String? = null, // defaults to frost title
|
||||
val text: String,
|
||||
val timestamp: Long,
|
||||
val profileUrl: String?) {
|
||||
data class NotificationContent(
|
||||
val data: CookieModel,
|
||||
val id: Long,
|
||||
val href: String,
|
||||
val title: String? = null, // defaults to frost title
|
||||
val text: String,
|
||||
val timestamp: Long,
|
||||
val profileUrl: String?
|
||||
) {
|
||||
|
||||
val notifId = Math.abs(id.toInt())
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a complete notification builder and identifier
|
||||
* which can be immediately notified when given a [Context]
|
||||
*/
|
||||
data class FrostNotification(private val tag: String,
|
||||
private val id: Int,
|
||||
val notif: NotificationCompat.Builder) {
|
||||
data class FrostNotification(
|
||||
private val tag: String,
|
||||
private val id: Int,
|
||||
val notif: NotificationCompat.Builder
|
||||
) {
|
||||
|
||||
fun withAlert(enable: Boolean, ringtone: String): FrostNotification {
|
||||
notif.setFrostAlert(enable, ringtone)
|
||||
@ -243,15 +271,15 @@ data class FrostNotification(private val tag: String,
|
||||
}
|
||||
|
||||
fun notify(context: Context) =
|
||||
NotificationManagerCompat.from(context).notify(tag, id, notif.build())
|
||||
NotificationManagerCompat.from(context).notify(tag, id, notif.build())
|
||||
}
|
||||
|
||||
const val NOTIFICATION_PERIODIC_JOB = 7
|
||||
|
||||
fun Context.scheduleNotifications(minutes: Long): Boolean =
|
||||
scheduleJob<NotificationService>(NOTIFICATION_PERIODIC_JOB, minutes)
|
||||
scheduleJob<NotificationService>(NOTIFICATION_PERIODIC_JOB, minutes)
|
||||
|
||||
const val NOTIFICATION_JOB_NOW = 6
|
||||
|
||||
fun Context.fetchNotifications(): Boolean =
|
||||
fetchJob<NotificationService>(NOTIFICATION_JOB_NOW)
|
||||
fetchJob<NotificationService>(NOTIFICATION_JOB_NOW)
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.app.job.JobInfo
|
||||
@ -37,10 +53,10 @@ private enum class FrostRequestCommands : EnumBundle<FrostRequestCommands> {
|
||||
}
|
||||
|
||||
override fun propagate(bundle: BaseBundle) =
|
||||
FrostRunnable.prepareMarkNotificationRead(
|
||||
bundle.getLong(ARG_0),
|
||||
bundle.getCookie())
|
||||
|
||||
FrostRunnable.prepareMarkNotificationRead(
|
||||
bundle.getLong(ARG_0),
|
||||
bundle.getCookie()
|
||||
)
|
||||
};
|
||||
|
||||
override val bundleContract: EnumBundleCompanion<FrostRequestCommands>
|
||||
@ -58,7 +74,6 @@ private enum class FrostRequestCommands : EnumBundle<FrostRequestCommands> {
|
||||
abstract fun propagate(bundle: BaseBundle): BaseBundle.() -> Unit
|
||||
|
||||
companion object : EnumCompanion<FrostRequestCommands>("frost_arg_commands", values())
|
||||
|
||||
}
|
||||
|
||||
private const val ARG_COMMAND = "frost_request_command"
|
||||
@ -99,8 +114,10 @@ object FrostRunnable {
|
||||
L.d { "Invalid notification id $id for marking as read" }
|
||||
return false
|
||||
}
|
||||
return schedule(context, FrostRequestCommands.NOTIF_READ,
|
||||
prepareMarkNotificationRead(id, cookie))
|
||||
return schedule(
|
||||
context, FrostRequestCommands.NOTIF_READ,
|
||||
prepareMarkNotificationRead(id, cookie)
|
||||
)
|
||||
}
|
||||
|
||||
fun propagate(context: Context, intent: Intent?) {
|
||||
@ -112,9 +129,11 @@ object FrostRunnable {
|
||||
schedule(context, command, builder)
|
||||
}
|
||||
|
||||
private fun schedule(context: Context,
|
||||
command: FrostRequestCommands,
|
||||
bundleBuilder: PersistableBundle.() -> Unit): Boolean {
|
||||
private fun schedule(
|
||||
context: Context,
|
||||
command: FrostRequestCommands,
|
||||
bundleBuilder: PersistableBundle.() -> Unit
|
||||
): Boolean {
|
||||
val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
|
||||
val serviceComponent = ComponentName(context, FrostRequestService::class.java)
|
||||
val bundle = PersistableBundle()
|
||||
@ -127,10 +146,10 @@ object FrostRunnable {
|
||||
}
|
||||
|
||||
val builder = JobInfo.Builder(JOB_REQUEST_BASE + command.ordinal, serviceComponent)
|
||||
.setMinimumLatency(0L)
|
||||
.setExtras(bundle)
|
||||
.setOverrideDeadline(2000L)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
.setMinimumLatency(0L)
|
||||
.setExtras(bundle)
|
||||
.setOverrideDeadline(2000L)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
val result = scheduler.schedule(builder.build())
|
||||
if (result <= 0) {
|
||||
L.eThrow("FrostRequestService scheduler failed for ${command.name}")
|
||||
@ -139,7 +158,6 @@ object FrostRunnable {
|
||||
L.d { "Scheduled ${command.name}" }
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FrostRequestService : JobService() {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.app.job.JobParameters
|
||||
@ -31,10 +47,12 @@ class NotificationService : JobService() {
|
||||
override fun onStopJob(params: JobParameters?): Boolean {
|
||||
val time = System.currentTimeMillis() - startTime
|
||||
L.d { "Notification service has finished abruptly in $time ms" }
|
||||
frostEvent("NotificationTime",
|
||||
"Type" to "Service force stop",
|
||||
"IM Included" to Prefs.notificationsInstantMessages,
|
||||
"Duration" to time)
|
||||
frostEvent(
|
||||
"NotificationTime",
|
||||
"Type" to "Service force stop",
|
||||
"IM Included" to Prefs.notificationsInstantMessages,
|
||||
"Duration" to time
|
||||
)
|
||||
future?.cancel(true)
|
||||
future = null
|
||||
return false
|
||||
@ -43,10 +61,12 @@ class NotificationService : JobService() {
|
||||
fun finish(params: JobParameters?) {
|
||||
val time = System.currentTimeMillis() - startTime
|
||||
L.i { "Notification service has finished in $time ms" }
|
||||
frostEvent("NotificationTime",
|
||||
"Type" to "Service",
|
||||
"IM Included" to Prefs.notificationsInstantMessages,
|
||||
"Duration" to time)
|
||||
frostEvent(
|
||||
"NotificationTime",
|
||||
"Type" to "Service",
|
||||
"IM Included" to Prefs.notificationsInstantMessages,
|
||||
"Duration" to time
|
||||
)
|
||||
jobFinished(params, false)
|
||||
future?.cancel(true)
|
||||
future = null
|
||||
@ -61,11 +81,13 @@ class NotificationService : JobService() {
|
||||
var notifCount = 0
|
||||
cookies.forEach {
|
||||
val current = it.id == currentId
|
||||
if (Prefs.notificationsGeneral
|
||||
&& (current || Prefs.notificationAllAccounts))
|
||||
if (Prefs.notificationsGeneral &&
|
||||
(current || Prefs.notificationAllAccounts)
|
||||
)
|
||||
notifCount += fetch(jobId, NotificationType.GENERAL, it)
|
||||
if (Prefs.notificationsInstantMessages
|
||||
&& (current || Prefs.notificationsImAllAccounts))
|
||||
if (Prefs.notificationsInstantMessages &&
|
||||
(current || Prefs.notificationsImAllAccounts)
|
||||
)
|
||||
notifCount += fetch(jobId, NotificationType.MESSAGE, it)
|
||||
}
|
||||
|
||||
@ -99,10 +121,9 @@ class NotificationService : JobService() {
|
||||
|
||||
private fun generalNotification(id: Int, textRes: Int, withDefaults: Boolean) {
|
||||
val notifBuilder = frostNotification(NOTIF_CHANNEL_GENERAL)
|
||||
.setFrostAlert(withDefaults, Prefs.notificationRingtone)
|
||||
.setContentTitle(string(R.string.frost_name))
|
||||
.setContentText(string(textRes))
|
||||
.setFrostAlert(withDefaults, Prefs.notificationRingtone)
|
||||
.setContentTitle(string(R.string.frost_name))
|
||||
.setContentText(string(textRes))
|
||||
NotificationManagerCompat.from(this).notify(id, notifBuilder.build())
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.app.Notification
|
||||
@ -31,11 +47,11 @@ fun setupNotificationChannels(c: Context) {
|
||||
val appName = c.string(R.string.frost_name)
|
||||
val msg = c.string(R.string.messages)
|
||||
manager.notificationChannels
|
||||
.filter {
|
||||
it.id != NOTIF_CHANNEL_GENERAL
|
||||
&& it.id != NOTIF_CHANNEL_MESSAGES
|
||||
}
|
||||
.forEach { manager.deleteNotificationChannel(it.id) }
|
||||
.filter {
|
||||
it.id != NOTIF_CHANNEL_GENERAL &&
|
||||
it.id != NOTIF_CHANNEL_MESSAGES
|
||||
}
|
||||
.forEach { manager.deleteNotificationChannel(it.id) }
|
||||
manager.createNotificationChannel(NOTIF_CHANNEL_GENERAL, appName)
|
||||
manager.createNotificationChannel(NOTIF_CHANNEL_MESSAGES, "$appName: $msg")
|
||||
L.d { "Created notification channels: ${manager.notificationChannels.size} channels, ${manager.notificationChannelGroups.size} groups" }
|
||||
@ -43,8 +59,10 @@ fun setupNotificationChannels(c: Context) {
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun NotificationManager.createNotificationChannel(id: String, name: String): NotificationChannel {
|
||||
val channel = NotificationChannel(id,
|
||||
name, NotificationManager.IMPORTANCE_DEFAULT)
|
||||
val channel = NotificationChannel(
|
||||
id,
|
||||
name, NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
channel.enableLights(true)
|
||||
channel.lightColor = Prefs.accentColor
|
||||
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
@ -53,14 +71,14 @@ private fun NotificationManager.createNotificationChannel(id: String, name: Stri
|
||||
}
|
||||
|
||||
fun Context.frostNotification(id: String) =
|
||||
NotificationCompat.Builder(this, id)
|
||||
.apply {
|
||||
setSmallIcon(R.drawable.frost_f_24)
|
||||
setAutoCancel(true)
|
||||
setOnlyAlertOnce(true)
|
||||
setStyle(NotificationCompat.BigTextStyle())
|
||||
color = color(R.color.frost_notification_accent)
|
||||
}
|
||||
NotificationCompat.Builder(this, id)
|
||||
.apply {
|
||||
setSmallIcon(R.drawable.frost_f_24)
|
||||
setAutoCancel(true)
|
||||
setOnlyAlertOnce(true)
|
||||
setStyle(NotificationCompat.BigTextStyle())
|
||||
color = color(R.color.frost_notification_accent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dictates whether a notification should have sound/vibration/lights or not
|
||||
@ -70,8 +88,9 @@ fun Context.frostNotification(id: String) =
|
||||
fun NotificationCompat.Builder.setFrostAlert(enable: Boolean, ringtone: String): NotificationCompat.Builder {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setGroupAlertBehavior(
|
||||
if (enable) NotificationCompat.GROUP_ALERT_CHILDREN
|
||||
else NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
if (enable) NotificationCompat.GROUP_ALERT_CHILDREN
|
||||
else NotificationCompat.GROUP_ALERT_SUMMARY
|
||||
)
|
||||
} else if (!enable) {
|
||||
setDefaults(0)
|
||||
} else {
|
||||
@ -111,10 +130,10 @@ inline fun <reified T : JobService> Context.scheduleJob(id: Int, minutes: Long):
|
||||
if (minutes < 0L) return true
|
||||
val serviceComponent = ComponentName(this, T::class.java)
|
||||
val builder = JobInfo.Builder(id, serviceComponent)
|
||||
.setPeriodic(minutes * 60000)
|
||||
.setExtras(id)
|
||||
.setPersisted(true)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options
|
||||
.setPeriodic(minutes * 60000)
|
||||
.setExtras(id)
|
||||
.setPersisted(true)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //TODO add options
|
||||
val result = scheduler.schedule(builder.build())
|
||||
if (result <= 0) {
|
||||
L.eThrow("${T::class.java.simpleName} scheduler failed")
|
||||
@ -130,10 +149,10 @@ inline fun <reified T : JobService> Context.fetchJob(id: Int): Boolean {
|
||||
val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
|
||||
val serviceComponent = ComponentName(this, T::class.java)
|
||||
val builder = JobInfo.Builder(id, serviceComponent)
|
||||
.setMinimumLatency(0L)
|
||||
.setExtras(id)
|
||||
.setOverrideDeadline(2000L)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
.setMinimumLatency(0L)
|
||||
.setExtras(id)
|
||||
.setOverrideDeadline(2000L)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
val result = scheduler.schedule(builder.build())
|
||||
if (result <= 0) {
|
||||
L.eThrow("${T::class.java.simpleName} instant scheduler failed")
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
@ -18,5 +34,4 @@ class UpdateReceiver : BroadcastReceiver() {
|
||||
L.d { "Frost has updated" }
|
||||
context.scheduleNotifications(Prefs.notificationFreq) //Update notifications
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
|
||||
@ -10,7 +26,16 @@ import com.pitchedapps.frost.activities.SettingsActivity
|
||||
import com.pitchedapps.frost.enums.MainActivityLayout
|
||||
import com.pitchedapps.frost.enums.Theme
|
||||
import com.pitchedapps.frost.injectors.CssAssets
|
||||
import com.pitchedapps.frost.utils.*
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.REQUEST_NAV
|
||||
import com.pitchedapps.frost.utils.REQUEST_REFRESH
|
||||
import com.pitchedapps.frost.utils.REQUEST_TEXT_ZOOM
|
||||
import com.pitchedapps.frost.utils.frostEvent
|
||||
import com.pitchedapps.frost.utils.frostNavigationBar
|
||||
import com.pitchedapps.frost.utils.frostSnackbar
|
||||
import com.pitchedapps.frost.utils.launchTabCustomizerActivity
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.utils.setFrostTheme
|
||||
import com.pitchedapps.frost.views.KPrefTextSeekbar
|
||||
|
||||
/**
|
||||
@ -74,7 +99,6 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
allowCustomAlpha = false
|
||||
}
|
||||
|
||||
|
||||
colorPicker(R.string.background_color, Prefs::customBackgroundColor, {
|
||||
Prefs.customBackgroundColor = it
|
||||
bgCanvas.ripple(it, duration = 500L)
|
||||
@ -146,13 +170,16 @@ fun SettingsActivity.getAppearancePrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
descRes = R.string.tint_nav_desc
|
||||
}
|
||||
|
||||
list.add(KPrefTextSeekbar(
|
||||
list.add(
|
||||
KPrefTextSeekbar(
|
||||
KPrefSeekbar.KPrefSeekbarBuilder(
|
||||
globalOptions,
|
||||
R.string.web_text_scaling, Prefs::webTextScaling) {
|
||||
globalOptions,
|
||||
R.string.web_text_scaling, Prefs::webTextScaling
|
||||
) {
|
||||
Prefs.webTextScaling = it
|
||||
setFrostResult(REQUEST_TEXT_ZOOM)
|
||||
}))
|
||||
})
|
||||
)
|
||||
|
||||
checkbox(R.string.enforce_black_media_bg, Prefs::blackMediaBg, {
|
||||
Prefs.blackMediaBg = it
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
|
||||
@ -15,7 +31,10 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
descRes = R.string.fancy_animations_desc
|
||||
}
|
||||
|
||||
checkbox(R.string.overlay_swipe, Prefs::overlayEnabled, { Prefs.overlayEnabled = it; setFrostResult(REQUEST_REFRESH) }) {
|
||||
checkbox(
|
||||
R.string.overlay_swipe,
|
||||
Prefs::overlayEnabled,
|
||||
{ Prefs.overlayEnabled = it; setFrostResult(REQUEST_REFRESH) }) {
|
||||
descRes = R.string.overlay_swipe_desc
|
||||
}
|
||||
|
||||
@ -46,5 +65,4 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
checkbox(R.string.analytics, Prefs::analytics, { Prefs.analytics = it }) {
|
||||
descRes = R.string.analytics_desc
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import android.content.Context
|
||||
@ -77,25 +93,26 @@ fun SettingsActivity.getDebugPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.createEmail(parser: FrostParser<*>, content: Any?) =
|
||||
sendFrostEmail("${string(R.string.debug_report)}: ${parser::class.java.simpleName}") {
|
||||
addItem("Url", parser.url)
|
||||
addItem("Contents", "$content")
|
||||
}
|
||||
sendFrostEmail("${string(R.string.debug_report)}: ${parser::class.java.simpleName}") {
|
||||
addItem("Url", parser.url)
|
||||
addItem("Contents", "$content")
|
||||
}
|
||||
|
||||
private const val ZIP_NAME = "debug"
|
||||
|
||||
fun SettingsActivity.sendDebug(url: String, html: String?) {
|
||||
|
||||
val downloader = OfflineWebsite(url, FbCookie.webCookie ?: "",
|
||||
baseUrl = FB_URL_BASE,
|
||||
html = html,
|
||||
baseDir = DebugActivity.baseDir(this))
|
||||
val downloader = OfflineWebsite(
|
||||
url, FbCookie.webCookie ?: "",
|
||||
baseUrl = FB_URL_BASE,
|
||||
html = html,
|
||||
baseDir = DebugActivity.baseDir(this)
|
||||
)
|
||||
|
||||
val md = materialDialog {
|
||||
title(R.string.parsing_data)
|
||||
@ -114,7 +131,8 @@ fun SettingsActivity.sendDebug(url: String, html: String?) {
|
||||
it.dismiss()
|
||||
if (success) {
|
||||
val zipUri = it.context.frostUriFromFile(
|
||||
File(downloader.baseDir, "$ZIP_NAME.zip"))
|
||||
File(downloader.baseDir, "$ZIP_NAME.zip")
|
||||
)
|
||||
L.i { "Sending debug zip with uri $zipUri" }
|
||||
sendFrostEmail(R.string.debug_report_email_title) {
|
||||
addItem("Url", url)
|
||||
@ -128,7 +146,5 @@ fun SettingsActivity.sendDebug(url: String, html: String?) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import android.util.Log
|
||||
@ -24,7 +40,6 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
|
||||
// Experimental content starts here ------------------
|
||||
|
||||
|
||||
// Experimental content ends here --------------------
|
||||
|
||||
checkbox(R.string.verbose_logging, Prefs::verboseLogging, {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder
|
||||
@ -10,8 +26,10 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
*/
|
||||
fun SettingsActivity.getNetworkPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
|
||||
checkbox(R.string.network_media_on_metered, { !Prefs.loadMediaOnMeteredNetwork }, { Prefs.loadMediaOnMeteredNetwork = !it }) {
|
||||
checkbox(
|
||||
R.string.network_media_on_metered,
|
||||
{ !Prefs.loadMediaOnMeteredNetwork },
|
||||
{ Prefs.loadMediaOnMeteredNetwork = !it }) {
|
||||
descRes = R.string.network_media_on_metered_desc
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -22,7 +38,6 @@ import com.pitchedapps.frost.utils.frostSnackbar
|
||||
import com.pitchedapps.frost.utils.materialDialogThemed
|
||||
import com.pitchedapps.frost.views.Keywords
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-29.
|
||||
*/
|
||||
@ -66,33 +81,33 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
}
|
||||
|
||||
checkbox(R.string.notification_general, Prefs::notificationsGeneral,
|
||||
{
|
||||
Prefs.notificationsGeneral = it
|
||||
reloadByTitle(R.string.notification_general_all_accounts)
|
||||
if (!Prefs.notificationsInstantMessages)
|
||||
reloadByTitle(R.string.notification_frequency)
|
||||
}) {
|
||||
{
|
||||
Prefs.notificationsGeneral = it
|
||||
reloadByTitle(R.string.notification_general_all_accounts)
|
||||
if (!Prefs.notificationsInstantMessages)
|
||||
reloadByTitle(R.string.notification_frequency)
|
||||
}) {
|
||||
descRes = R.string.notification_general_desc
|
||||
}
|
||||
|
||||
checkbox(R.string.notification_general_all_accounts, Prefs::notificationAllAccounts,
|
||||
{ Prefs.notificationAllAccounts = it }) {
|
||||
{ Prefs.notificationAllAccounts = it }) {
|
||||
descRes = R.string.notification_general_all_accounts_desc
|
||||
enabler = Prefs::notificationsGeneral
|
||||
}
|
||||
|
||||
checkbox(R.string.notification_messages, Prefs::notificationsInstantMessages,
|
||||
{
|
||||
Prefs.notificationsInstantMessages = it
|
||||
reloadByTitle(R.string.notification_messages_all_accounts)
|
||||
if (!Prefs.notificationsGeneral)
|
||||
reloadByTitle(R.string.notification_frequency)
|
||||
}) {
|
||||
{
|
||||
Prefs.notificationsInstantMessages = it
|
||||
reloadByTitle(R.string.notification_messages_all_accounts)
|
||||
if (!Prefs.notificationsGeneral)
|
||||
reloadByTitle(R.string.notification_frequency)
|
||||
}) {
|
||||
descRes = R.string.notification_messages_desc
|
||||
}
|
||||
|
||||
checkbox(R.string.notification_messages_all_accounts, Prefs::notificationsImAllAccounts,
|
||||
{ Prefs.notificationsImAllAccounts = it }) {
|
||||
{ Prefs.notificationsImAllAccounts = it }) {
|
||||
descRes = R.string.notification_messages_all_accounts_desc
|
||||
enabler = Prefs::notificationsInstantMessages
|
||||
}
|
||||
@ -102,15 +117,17 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
descRes = R.string.notification_channel_desc
|
||||
onClick = {
|
||||
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||
.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
||||
.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkbox(R.string.notification_sound, Prefs::notificationSound, {
|
||||
Prefs.notificationSound = it
|
||||
reloadByTitle(R.string.notification_ringtone,
|
||||
R.string.message_ringtone)
|
||||
reloadByTitle(
|
||||
R.string.notification_ringtone,
|
||||
R.string.message_ringtone
|
||||
)
|
||||
})
|
||||
|
||||
fun KPrefText.KPrefTextContract<String>.ringtone(code: Int) {
|
||||
@ -118,8 +135,8 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
textGetter = {
|
||||
if (it.isBlank()) string(R.string.kau_default)
|
||||
else RingtoneManager.getRingtone(this@getNotificationPrefs, Uri.parse(it))
|
||||
?.getTitle(this@getNotificationPrefs)
|
||||
?: "---" //todo figure out why this happens
|
||||
?.getTitle(this@getNotificationPrefs)
|
||||
?: "---" //todo figure out why this happens
|
||||
}
|
||||
onClick = {
|
||||
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||
@ -135,20 +152,20 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
}
|
||||
|
||||
text(R.string.notification_ringtone, Prefs::notificationRingtone,
|
||||
{ Prefs.notificationRingtone = it }) {
|
||||
{ Prefs.notificationRingtone = it }) {
|
||||
ringtone(SettingsActivity.REQUEST_NOTIFICATION_RINGTONE)
|
||||
}
|
||||
|
||||
text(R.string.message_ringtone, Prefs::messageRingtone,
|
||||
{ Prefs.messageRingtone = it }) {
|
||||
{ Prefs.messageRingtone = it }) {
|
||||
ringtone(SettingsActivity.REQUEST_MESSAGE_RINGTONE)
|
||||
}
|
||||
|
||||
checkbox(R.string.notification_vibrate, Prefs::notificationVibrate,
|
||||
{ Prefs.notificationVibrate = it })
|
||||
{ Prefs.notificationVibrate = it })
|
||||
|
||||
checkbox(R.string.notification_lights, Prefs::notificationLights,
|
||||
{ Prefs.notificationLights = it })
|
||||
{ Prefs.notificationLights = it })
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
@ -165,10 +182,9 @@ fun SettingsActivity.getNotificationPrefs(): KPrefAdapterBuilder.() -> Unit = {
|
||||
descRes = R.string.notification_fetch_now_desc
|
||||
onClick = {
|
||||
val text =
|
||||
if (fetchNotifications()) R.string.notification_fetch_success
|
||||
else R.string.notification_fetch_fail
|
||||
if (fetchNotifications()) R.string.notification_fetch_success
|
||||
else R.string.notification_fetch_fail
|
||||
frostSnackbar(text)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.content.Context
|
||||
|
@ -1,8 +1,24 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.graphics.drawable.AnimatedVectorDrawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import ca.allanwang.kau.utils.drawable
|
||||
|
||||
/**
|
||||
@ -23,23 +39,23 @@ interface AnimatedVectorContract {
|
||||
}
|
||||
|
||||
class AnimatedVectorDelegate(
|
||||
/**
|
||||
* The res for the starting resource; must have parent tag animated-vector
|
||||
*/
|
||||
@param:DrawableRes val avdStart: Int,
|
||||
/**
|
||||
* The res for the ending resource; must have parent tag animated-vector
|
||||
*/
|
||||
@param:DrawableRes val avdEnd: Int,
|
||||
/**
|
||||
* The delegate will automatically set the start resource when bound
|
||||
* If [emitOnBind] is true, it will also trigger the listener
|
||||
*/
|
||||
val emitOnBind: Boolean = true,
|
||||
/**
|
||||
* The optional listener that will be triggered every time the avd is switched by the delegate
|
||||
*/
|
||||
override var animatedVectorListener: ((avd: AnimatedVectorDrawable, forwards: Boolean) -> Unit)? = null
|
||||
/**
|
||||
* The res for the starting resource; must have parent tag animated-vector
|
||||
*/
|
||||
@param:DrawableRes val avdStart: Int,
|
||||
/**
|
||||
* The res for the ending resource; must have parent tag animated-vector
|
||||
*/
|
||||
@param:DrawableRes val avdEnd: Int,
|
||||
/**
|
||||
* The delegate will automatically set the start resource when bound
|
||||
* If [emitOnBind] is true, it will also trigger the listener
|
||||
*/
|
||||
val emitOnBind: Boolean = true,
|
||||
/**
|
||||
* The optional listener that will be triggered every time the avd is switched by the delegate
|
||||
*/
|
||||
override var animatedVectorListener: ((avd: AnimatedVectorDrawable, forwards: Boolean) -> Unit)? = null
|
||||
) : AnimatedVectorContract {
|
||||
|
||||
lateinit var view: ImageView
|
||||
@ -55,9 +71,9 @@ 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")
|
||||
?: 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")
|
||||
?: throw IllegalArgumentException("AnimatedVectorDelegate has an ending drawable that isn't an avd")
|
||||
view.setImageResource(avdStart)
|
||||
if (emitOnBind) animatedVectorListener?.invoke(avd!!, false)
|
||||
}
|
||||
@ -70,15 +86,11 @@ class AnimatedVectorDelegate(
|
||||
|
||||
private fun animateImpl(toStart: Boolean) {
|
||||
if ((atStart == toStart)) return L.d { "AVD already at ${if (toStart) "start" else "end"}" }
|
||||
if (avd == null) return L.d { "AVD null resource" }//no longer using animated vector; do not modify
|
||||
if (avd == null) return L.d { "AVD null resource" } //no longer using animated vector; do not modify
|
||||
avd?.stop()
|
||||
view.setImageResource(if (toStart) avdEnd else avdStart)
|
||||
animatedVectorListener?.invoke(avd!!, !toStart)
|
||||
atStart = toStart
|
||||
avd?.start()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
object BuildUtils {
|
||||
@ -18,8 +34,7 @@ object BuildUtils {
|
||||
}
|
||||
|
||||
fun getAllStages(): Array<String> =
|
||||
arrayOf(BUILD_PRODUCTION, BUILD_TEST, BUILD_GITHUB, BUILD_RELEASE, BUILD_UNNAMED)
|
||||
arrayOf(BUILD_PRODUCTION, BUILD_TEST, BUILD_GITHUB, BUILD_RELEASE, BUILD_UNNAMED)
|
||||
|
||||
fun getStage(build: String): String = build.takeIf { it in getAllStages() } ?: BUILD_UNNAMED
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.app.DownloadManager
|
||||
@ -16,26 +32,29 @@ import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.dbflow.loadFbCookie
|
||||
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-08-04.
|
||||
*
|
||||
* With reference to <a href="https://stackoverflow.com/questions/33434532/android-webview-download-files-like-browsers-do">Stack Overflow</a>
|
||||
*/
|
||||
fun Context.frostDownload(url: String?,
|
||||
userAgent: String = USER_AGENT_BASIC,
|
||||
contentDisposition: String? = null,
|
||||
mimeType: String? = null,
|
||||
contentLength: Long = 0L) {
|
||||
fun Context.frostDownload(
|
||||
url: String?,
|
||||
userAgent: String = USER_AGENT_BASIC,
|
||||
contentDisposition: String? = null,
|
||||
mimeType: String? = null,
|
||||
contentLength: Long = 0L
|
||||
) {
|
||||
url ?: return
|
||||
frostDownload(Uri.parse(url), userAgent, contentDisposition, mimeType, contentLength)
|
||||
}
|
||||
|
||||
fun Context.frostDownload(uri: Uri?,
|
||||
userAgent: String = USER_AGENT_BASIC,
|
||||
contentDisposition: String? = null,
|
||||
mimeType: String? = null,
|
||||
contentLength: Long = 0L) {
|
||||
fun Context.frostDownload(
|
||||
uri: Uri?,
|
||||
userAgent: String = USER_AGENT_BASIC,
|
||||
contentDisposition: String? = null,
|
||||
mimeType: String? = null,
|
||||
contentLength: Long = 0L
|
||||
) {
|
||||
uri ?: return
|
||||
L.d { "Received download request" }
|
||||
if (uri.scheme != "http" && uri.scheme != "https") {
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.content.Intent
|
||||
@ -39,12 +55,12 @@ interface EnumBundleCompanion<E : Enum<E>> {
|
||||
operator fun get(bundle: BaseBundle?) = get(bundle?.getString(argTag))
|
||||
|
||||
operator fun get(intent: Intent?) = get(intent?.getStringExtra(argTag))
|
||||
|
||||
}
|
||||
|
||||
open class EnumCompanion<E : Enum<E>>(
|
||||
final override val argTag: String,
|
||||
final override val values: Array<E>) : EnumBundleCompanion<E> {
|
||||
final override val argTag: String,
|
||||
final override val values: Array<E>
|
||||
) : EnumBundleCompanion<E> {
|
||||
|
||||
final override val valueMap: Map<String, E> = values.map { it.name to it }.toMap()
|
||||
|
||||
@ -53,5 +69,4 @@ open class EnumCompanion<E : Enum<E>>(
|
||||
final override fun get(bundle: BaseBundle?) = super.get(bundle)
|
||||
|
||||
final override fun get(intent: Intent?) = super.get(intent)
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import org.jsoup.Jsoup
|
||||
@ -21,8 +37,10 @@ internal fun String.cleanJsoup(): String = Jsoup.clean(this, PrivacyWhitelist())
|
||||
class PrivacyWhitelist : Whitelist() {
|
||||
|
||||
val blacklistAttrs = arrayOf("style", "aria-label", "rel")
|
||||
val blacklistTags = arrayOf("body", "html", "head", "i", "b", "u", "style", "script",
|
||||
"br", "p", "span", "ul", "ol", "li")
|
||||
val blacklistTags = arrayOf(
|
||||
"body", "html", "head", "i", "b", "u", "style", "script",
|
||||
"br", "p", "span", "ul", "ol", "li"
|
||||
)
|
||||
|
||||
override fun isSafeAttribute(tagName: String, el: Element, attr: Attribute): Boolean {
|
||||
val key = attr.key
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.util.Log
|
||||
@ -5,7 +21,6 @@ import ca.allanwang.kau.logging.KauLogger
|
||||
import com.bugsnag.android.Bugsnag
|
||||
import com.pitchedapps.frost.BuildConfig
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-28.
|
||||
*
|
||||
@ -45,5 +60,4 @@ object L : KauLogger("Frost", {
|
||||
Bugsnag.notify(t)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.graphics.Color
|
||||
@ -71,8 +87,8 @@ object Prefs : KPref() {
|
||||
get() = Prefs.bgColor.withAlpha(30)
|
||||
|
||||
fun nativeBgColor(unread: Boolean) = Prefs.bgColor
|
||||
.colorToForeground(if (unread) 0.7f else 0.0f)
|
||||
.withAlpha(30)
|
||||
.colorToForeground(if (unread) 0.7f else 0.0f)
|
||||
.withAlpha(30)
|
||||
|
||||
val bgColor: Int
|
||||
get() = t.bgColor
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import ca.allanwang.kau.kpref.KPref
|
||||
@ -21,4 +37,3 @@ object Showcase : KPref() {
|
||||
|
||||
override fun deleteKeys() = arrayOf("shown_release")
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -8,33 +24,59 @@ import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.FileProvider
|
||||
import ca.allanwang.kau.email.EmailBuilder
|
||||
import ca.allanwang.kau.email.sendEmail
|
||||
import ca.allanwang.kau.mediapicker.createMediaFile
|
||||
import ca.allanwang.kau.mediapicker.createPrivateMediaFile
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.adjustAlpha
|
||||
import ca.allanwang.kau.utils.colorToForeground
|
||||
import ca.allanwang.kau.utils.darken
|
||||
import ca.allanwang.kau.utils.isColorDark
|
||||
import ca.allanwang.kau.utils.lighten
|
||||
import ca.allanwang.kau.utils.navigationBarColor
|
||||
import ca.allanwang.kau.utils.snackbar
|
||||
import ca.allanwang.kau.utils.startActivity
|
||||
import ca.allanwang.kau.utils.startActivityForResult
|
||||
import ca.allanwang.kau.utils.statusBarColor
|
||||
import ca.allanwang.kau.utils.string
|
||||
import ca.allanwang.kau.utils.with
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import ca.allanwang.kau.utils.withMinAlpha
|
||||
import ca.allanwang.kau.xml.showChangelog
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.snackbar.SnackbarContentLayout
|
||||
import com.pitchedapps.frost.BuildConfig
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.activities.*
|
||||
import com.pitchedapps.frost.activities.ImageActivity
|
||||
import com.pitchedapps.frost.activities.LoginActivity
|
||||
import com.pitchedapps.frost.activities.SelectorActivity
|
||||
import com.pitchedapps.frost.activities.SettingsActivity
|
||||
import com.pitchedapps.frost.activities.TabCustomizerActivity
|
||||
import com.pitchedapps.frost.activities.WebOverlayActivity
|
||||
import com.pitchedapps.frost.activities.WebOverlayActivityBase
|
||||
import com.pitchedapps.frost.activities.WebOverlayBasicActivity
|
||||
import com.pitchedapps.frost.dbflow.CookieModel
|
||||
import com.pitchedapps.frost.facebook.*
|
||||
import com.pitchedapps.frost.facebook.FACEBOOK_COM
|
||||
import com.pitchedapps.frost.facebook.FBCDN_NET
|
||||
import com.pitchedapps.frost.facebook.FbCookie
|
||||
import com.pitchedapps.frost.facebook.FbItem
|
||||
import com.pitchedapps.frost.facebook.FbUrlFormatter.Companion.VIDEO_REDIRECT
|
||||
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
import com.pitchedapps.frost.facebook.formattedFbUrl
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-03.
|
||||
@ -46,7 +88,10 @@ const val ARG_IMAGE_URL = "arg_image_url"
|
||||
const val ARG_TEXT = "arg_text"
|
||||
const val ARG_COOKIE = "arg_cookie"
|
||||
|
||||
inline fun <reified T : Activity> Context.launchNewTask(cookieList: ArrayList<CookieModel> = arrayListOf(), clearStack: Boolean = false) {
|
||||
inline fun <reified T : Activity> Context.launchNewTask(
|
||||
cookieList: ArrayList<CookieModel> = arrayListOf(),
|
||||
clearStack: Boolean = false
|
||||
) {
|
||||
startActivity<T>(clearStack, intentBuilder = {
|
||||
putParcelableArrayListExtra(EXTRA_COOKIES, cookieList)
|
||||
})
|
||||
@ -81,8 +126,10 @@ fun Context.launchWebOverlay(url: String) = launchWebOverlayImpl<WebOverlayActiv
|
||||
|
||||
fun Context.launchWebOverlayBasic(url: String) = launchWebOverlayImpl<WebOverlayBasicActivity>(url)
|
||||
|
||||
private fun Context.fadeBundle() = ActivityOptions.makeCustomAnimation(this,
|
||||
android.R.anim.fade_in, android.R.anim.fade_out).toBundle()
|
||||
private fun Context.fadeBundle() = ActivityOptions.makeCustomAnimation(
|
||||
this,
|
||||
android.R.anim.fade_in, android.R.anim.fade_out
|
||||
).toBundle()
|
||||
|
||||
fun Context.launchImageActivity(imageUrl: String, text: String? = null, cookie: String? = null) {
|
||||
startActivity<ImageActivity>(intentBuilder = {
|
||||
@ -174,7 +221,6 @@ inline fun Activity.setFrostColors(builder: ActivityThemeUtils.() -> Unit) {
|
||||
themer.theme(this)
|
||||
}
|
||||
|
||||
|
||||
fun frostEvent(name: String, vararg events: Pair<String, Any>) {
|
||||
// todo bind
|
||||
L.v { "Event: $name ${events.joinToString(", ")}" }
|
||||
@ -189,9 +235,11 @@ fun Throwable?.logFrostEvent(text: String) {
|
||||
frostEvent("Errors", "text" to text, "message" to (this?.message ?: "NA"))
|
||||
}
|
||||
|
||||
fun Activity.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {}) = snackbar(text, Snackbar.LENGTH_LONG, frostSnackbar(builder))
|
||||
fun Activity.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {}) =
|
||||
snackbar(text, Snackbar.LENGTH_LONG, frostSnackbar(builder))
|
||||
|
||||
fun View.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {}) = snackbar(text, Snackbar.LENGTH_LONG, frostSnackbar(builder))
|
||||
fun View.frostSnackbar(@StringRes text: Int, builder: Snackbar.() -> Unit = {}) =
|
||||
snackbar(text, Snackbar.LENGTH_LONG, frostSnackbar(builder))
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
private inline fun frostSnackbar(crossinline builder: Snackbar.() -> Unit): Snackbar.() -> Unit = {
|
||||
@ -240,7 +288,7 @@ inline val String?.isFacebookUrl
|
||||
*/
|
||||
inline val String.isVideoUrl
|
||||
get() = startsWith(VIDEO_REDIRECT) ||
|
||||
(startsWith("https://video-") && contains(FBCDN_NET))
|
||||
(startsWith("https://video-") && contains(FBCDN_NET))
|
||||
|
||||
/**
|
||||
* [true] if url directly leads to a usable image
|
||||
@ -271,22 +319,22 @@ inline val String?.isIndependent: Boolean
|
||||
}
|
||||
|
||||
val dependentSegments = arrayOf(
|
||||
"photoset_token", "direct_action_execute", "messages/?pageNum", "sharer.php",
|
||||
"events/permalink", "events/feed/watch",
|
||||
/*
|
||||
* Add new members to groups
|
||||
*/
|
||||
"madminpanel",
|
||||
/**
|
||||
* Editing images
|
||||
*/
|
||||
"/confirmation/?",
|
||||
/*
|
||||
* Facebook messages have the following cases for the tid query
|
||||
* mid* or id* for newer threads, which can be launched in new windows
|
||||
* or a hash for old threads, which must be loaded on old threads
|
||||
*/
|
||||
"messages/read/?tid=id", "messages/read/?tid=mid"
|
||||
"photoset_token", "direct_action_execute", "messages/?pageNum", "sharer.php",
|
||||
"events/permalink", "events/feed/watch",
|
||||
/*
|
||||
* Add new members to groups
|
||||
*/
|
||||
"madminpanel",
|
||||
/**
|
||||
* Editing images
|
||||
*/
|
||||
"/confirmation/?",
|
||||
/*
|
||||
* Facebook messages have the following cases for the tid query
|
||||
* mid* or id* for newer threads, which can be launched in new windows
|
||||
* or a hash for old threads, which must be loaded on old threads
|
||||
*/
|
||||
"messages/read/?tid=id", "messages/read/?tid=mid"
|
||||
)
|
||||
|
||||
inline val String?.isExplicitIntent
|
||||
@ -297,16 +345,20 @@ fun Context.frostChangelog() = showChangelog(R.xml.frost_changelog, Prefs.textCo
|
||||
}
|
||||
|
||||
fun Context.frostUriFromFile(file: File): Uri =
|
||||
FileProvider.getUriForFile(this,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
file)
|
||||
FileProvider.getUriForFile(
|
||||
this,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
file
|
||||
)
|
||||
|
||||
inline fun Context.sendFrostEmail(@StringRes subjectId: Int, crossinline builder: EmailBuilder.() -> Unit) = sendFrostEmail(string(subjectId), builder)
|
||||
inline fun Context.sendFrostEmail(@StringRes subjectId: Int, crossinline builder: EmailBuilder.() -> Unit) =
|
||||
sendFrostEmail(string(subjectId), builder)
|
||||
|
||||
inline fun Context.sendFrostEmail(subjectId: String, crossinline builder: EmailBuilder.() -> Unit) = sendEmail(string(R.string.dev_email), subjectId) {
|
||||
builder()
|
||||
addFrostDetails()
|
||||
}
|
||||
inline fun Context.sendFrostEmail(subjectId: String, crossinline builder: EmailBuilder.() -> Unit) =
|
||||
sendEmail(string(R.string.dev_email), subjectId) {
|
||||
builder()
|
||||
addFrostDetails()
|
||||
}
|
||||
|
||||
fun EmailBuilder.addFrostDetails() {
|
||||
addItem("Prev version", Prefs.prevVersionCode.toString())
|
||||
@ -318,7 +370,8 @@ fun EmailBuilder.addFrostDetails() {
|
||||
|
||||
fun frostJsoup(url: String) = frostJsoup(FbCookie.webCookie, url)
|
||||
|
||||
fun frostJsoup(cookie: String?, url: String) = Jsoup.connect(url).cookie(FACEBOOK_COM, cookie).userAgent(USER_AGENT_BASIC).get()!!
|
||||
fun frostJsoup(cookie: String?, url: String) =
|
||||
Jsoup.connect(url).cookie(FACEBOOK_COM, cookie).userAgent(USER_AGENT_BASIC).get()!!
|
||||
|
||||
fun Element.first(vararg select: String): Element? {
|
||||
select.forEach {
|
||||
@ -346,6 +399,6 @@ fun File.createFreshDir(): Boolean {
|
||||
}
|
||||
|
||||
fun String.unescapeHtml(): String =
|
||||
StringEscapeUtils.unescapeXml(this)
|
||||
.replace("\\u003C", "<")
|
||||
.replace("\\\"", "\"")
|
||||
StringEscapeUtils.unescapeXml(this)
|
||||
.replace("\\u003C", "<")
|
||||
.replace("\\\"", "\"")
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.utils
|
||||
|
||||
import android.content.Context
|
||||
@ -41,7 +57,9 @@ class WebContext(val unformattedUrl: String, val text: String?) {
|
||||
enum class WebContextType(val textId: Int, val onClick: (c: Context, wc: WebContext) -> Unit) {
|
||||
OPEN_LINK(R.string.open_link, { c, wc -> c.launchWebOverlay(wc.unformattedUrl) }),
|
||||
COPY_LINK(R.string.copy_link, { c, wc -> c.copyToClipboard(wc.url) }),
|
||||
COPY_TEXT(R.string.copy_text, { c, wc -> if (wc.text != null) c.copyToClipboard(wc.text) else c.toast(R.string.no_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) }),
|
||||
DEBUG_LINK(R.string.debug_link, { c, wc ->
|
||||
c.materialDialogThemed {
|
||||
|
@ -1,12 +1,32 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ca.allanwang.kau.iitems.KauIItem
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.fadeIn
|
||||
import ca.allanwang.kau.utils.invisible
|
||||
import ca.allanwang.kau.utils.toDrawable
|
||||
import ca.allanwang.kau.utils.visible
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
@ -23,7 +43,7 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
* Created by Allan Wang on 2017-06-05.
|
||||
*/
|
||||
class AccountItem(val cookie: CookieModel?) : KauIItem<AccountItem, AccountItem.ViewHolder>
|
||||
(R.layout.view_account, { ViewHolder(it) }, R.id.item_account) {
|
||||
(R.layout.view_account, { ViewHolder(it) }, R.id.item_account) {
|
||||
|
||||
override fun bindView(viewHolder: ViewHolder, payloads: MutableList<Any>) {
|
||||
super.bindView(viewHolder, payloads)
|
||||
@ -33,20 +53,37 @@ class AccountItem(val cookie: CookieModel?) : KauIItem<AccountItem, AccountItem.
|
||||
if (cookie != null) {
|
||||
text.text = cookie.name
|
||||
GlideApp.with(itemView).load(profilePictureUrl(cookie.id))
|
||||
.transform(FrostGlide.roundCorner).listener(object : RequestListener<Drawable> {
|
||||
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
text.fadeIn()
|
||||
return false
|
||||
}
|
||||
.transform(FrostGlide.roundCorner).listener(object : RequestListener<Drawable> {
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
text.fadeIn()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
|
||||
text.fadeIn()
|
||||
return false
|
||||
}
|
||||
}).into(image)
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
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))
|
||||
image.setImageDrawable(
|
||||
GoogleMaterial.Icon.gmd_add_circle_outline.toDrawable(
|
||||
itemView.context,
|
||||
100,
|
||||
Prefs.textColor
|
||||
)
|
||||
)
|
||||
text.text = itemView.context.getString(R.string.kau_add_account)
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,50 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import android.util.AttributeSet
|
||||
import ca.allanwang.kau.utils.*
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import ca.allanwang.kau.utils.colorToForeground
|
||||
import ca.allanwang.kau.utils.dpToPx
|
||||
import ca.allanwang.kau.utils.gone
|
||||
import ca.allanwang.kau.utils.toDrawable
|
||||
import ca.allanwang.kau.utils.visible
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import kotlinx.android.synthetic.main.view_badged_icon.view.*
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-19.
|
||||
*/
|
||||
class BadgedIcon @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_badged_icon, this)
|
||||
val badgeColor = Prefs.mainActivityLayout.backgroundColor().withAlpha(255).colorToForeground(0.2f)
|
||||
val badgeBackground = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(badgeColor, badgeColor))
|
||||
val badgeBackground =
|
||||
GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(badgeColor, badgeColor))
|
||||
badgeBackground.cornerRadius = 13.dpToPx.toFloat()
|
||||
badge_text.background = badgeBackground
|
||||
badge_text.setTextColor(Prefs.mainActivityLayout.iconColor())
|
||||
@ -30,7 +53,13 @@ class BadgedIcon @JvmOverloads constructor(
|
||||
var iicon: IIcon? = null
|
||||
set(value) {
|
||||
field = value
|
||||
badge_image.setImageDrawable(value?.toDrawable(context, sizeDp = 20, color = Prefs.mainActivityLayout.iconColor()))
|
||||
badge_image.setImageDrawable(
|
||||
value?.toDrawable(
|
||||
context,
|
||||
sizeDp = 20,
|
||||
color = Prefs.mainActivityLayout.iconColor()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun setAllAlpha(alpha: Float) {
|
||||
@ -46,5 +75,4 @@ class BadgedIcon @JvmOverloads constructor(
|
||||
if (value != null && value != "0") badge_text.visible()
|
||||
else badge_text.gone()
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,36 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ProgressBar
|
||||
import ca.allanwang.kau.utils.*
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.circularReveal
|
||||
import ca.allanwang.kau.utils.fadeIn
|
||||
import ca.allanwang.kau.utils.fadeOut
|
||||
import ca.allanwang.kau.utils.invisibleIf
|
||||
import ca.allanwang.kau.utils.isVisible
|
||||
import ca.allanwang.kau.utils.tint
|
||||
import ca.allanwang.kau.utils.withAlpha
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.contracts.FrostContentContainer
|
||||
import com.pitchedapps.frost.contracts.FrostContentCore
|
||||
@ -24,25 +47,32 @@ import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
|
||||
class FrostContentWeb @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : FrostContentView<FrostWebView>(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
override val layoutRes: Int = R.layout.view_content_base_web
|
||||
|
||||
}
|
||||
|
||||
class FrostContentRecycler @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : FrostContentView<FrostRecyclerView>(context, attrs, defStyleAttr, defStyleRes) {
|
||||
|
||||
override val layoutRes: Int = R.layout.view_content_base_recycler
|
||||
|
||||
}
|
||||
|
||||
abstract class FrostContentView<out T> @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
defStyleRes: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes),
|
||||
FrostContentParent where T : View, T : FrostContentCore {
|
||||
FrostContentParent where T : View, T : FrostContentCore {
|
||||
|
||||
private val refresh: SwipeRefreshLayout by bindView(R.id.content_refresh)
|
||||
private val progress: ProgressBar by bindView(R.id.content_progress)
|
||||
@ -88,15 +118,14 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
|
||||
}.addTo(compositeDisposable)
|
||||
|
||||
refreshObservable
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
refresh.isRefreshing = it
|
||||
refresh.isEnabled = true
|
||||
}.addTo(compositeDisposable)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
refresh.isRefreshing = it
|
||||
refresh.isEnabled = true
|
||||
}.addTo(compositeDisposable)
|
||||
refresh.setOnRefreshListener { coreView.reload(true) }
|
||||
|
||||
reloadThemeSelf()
|
||||
|
||||
}
|
||||
|
||||
override fun bind(container: FrostContentContainer) {
|
||||
@ -151,23 +180,23 @@ abstract class FrostContentView<out T> @JvmOverloads constructor(
|
||||
var loading = dispose != null
|
||||
dispose?.dispose()
|
||||
dispose = refreshObservable
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it) {
|
||||
loading = true
|
||||
transitionStart = System.currentTimeMillis()
|
||||
clearAnimation()
|
||||
if (isVisible)
|
||||
fadeOut(duration = 200L)
|
||||
} else if (loading) {
|
||||
loading = false
|
||||
if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
|
||||
else fadeIn(duration = 200L, offset = WEB_LOAD_DELAY)
|
||||
L.v { "Transition loaded in ${System.currentTimeMillis() - transitionStart} ms" }
|
||||
dispose?.dispose()
|
||||
dispose = null
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it) {
|
||||
loading = true
|
||||
transitionStart = System.currentTimeMillis()
|
||||
clearAnimation()
|
||||
if (isVisible)
|
||||
fadeOut(duration = 200L)
|
||||
} else if (loading) {
|
||||
loading = false
|
||||
if (animate && Prefs.animate) circularReveal(offset = WEB_LOAD_DELAY)
|
||||
else fadeIn(duration = 200L, offset = WEB_LOAD_DELAY)
|
||||
L.v { "Transition loaded in ${System.currentTimeMillis() - transitionStart} ms" }
|
||||
dispose?.dispose()
|
||||
dispose = null
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,10 +1,26 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.content.Context
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ca.allanwang.kau.utils.circularReveal
|
||||
import ca.allanwang.kau.utils.fadeOut
|
||||
import com.pitchedapps.frost.contracts.FrostContentContainer
|
||||
@ -18,9 +34,11 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
*
|
||||
*/
|
||||
class FrostRecyclerView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RecyclerView(context, attrs, defStyleAttr),
|
||||
FrostContentCore {
|
||||
FrostContentCore {
|
||||
|
||||
override fun reload(animate: Boolean) = reloadBase(animate)
|
||||
|
||||
@ -102,5 +120,4 @@ class FrostRecyclerView @JvmOverloads constructor(
|
||||
override fun reloadTextSizeSelf() {
|
||||
// todo
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
@ -27,7 +43,9 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
* Parent must have layout with both height & width as match_parent
|
||||
*/
|
||||
class FrostVideoView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : VideoView(context, attrs, defStyleAttr) {
|
||||
|
||||
/**
|
||||
@ -105,7 +123,10 @@ class FrostVideoView @JvmOverloads constructor(
|
||||
videoDimensions.set(dimen, dimen)
|
||||
}
|
||||
val portrait = height > width
|
||||
val scale = Math.min(height / (if (portrait) 4f else 2.3f) / videoDimensions.y, width / (if (portrait) 2.3f else 4f) / videoDimensions.x)
|
||||
val scale = Math.min(
|
||||
height / (if (portrait) 4f else 2.3f) / videoDimensions.y,
|
||||
width / (if (portrait) 2.3f else 4f) / videoDimensions.x
|
||||
)
|
||||
val desiredHeight = scale * videoDimensions.y
|
||||
val desiredWidth = scale * videoDimensions.x
|
||||
val padding = containerContract.lowerVideoPadding
|
||||
@ -151,8 +172,8 @@ class FrostVideoView @JvmOverloads constructor(
|
||||
/**
|
||||
* Only remap if not expanded and if dimensions have changed
|
||||
*/
|
||||
val shouldRemap = !isExpanded
|
||||
&& (videoDimensions.x != ratio * intrinsicWidth || videoDimensions.y != ratio * intrinsicHeight)
|
||||
val shouldRemap = !isExpanded &&
|
||||
(videoDimensions.x != ratio * intrinsicWidth || videoDimensions.y != ratio * intrinsicHeight)
|
||||
videoDimensions.set(ratio * intrinsicWidth, ratio * intrinsicHeight)
|
||||
if (shouldRemap) updateLocation()
|
||||
}
|
||||
@ -226,7 +247,8 @@ class FrostVideoView @JvmOverloads constructor(
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
private inner class FrameTouchListener(context: Context) : GestureDetector.SimpleOnGestureListener(), View.OnTouchListener {
|
||||
private inner class FrameTouchListener(context: Context) : GestureDetector.SimpleOnGestureListener(),
|
||||
View.OnTouchListener {
|
||||
|
||||
private val gestureDetector: GestureDetector = GestureDetector(context, this)
|
||||
|
||||
@ -252,7 +274,8 @@ class FrostVideoView @JvmOverloads constructor(
|
||||
/**
|
||||
* Monitors the view click events to show and hide the video controls if they have been specified.
|
||||
*/
|
||||
private inner class VideoTouchListener(context: Context) : GestureDetector.SimpleOnGestureListener(), View.OnTouchListener {
|
||||
private inner class VideoTouchListener(context: Context) : GestureDetector.SimpleOnGestureListener(),
|
||||
View.OnTouchListener {
|
||||
|
||||
private val gestureDetector: GestureDetector = GestureDetector(context, this)
|
||||
private val downLoc = PointF()
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.content.Context
|
||||
@ -8,11 +24,22 @@ import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.FrameLayout
|
||||
import ca.allanwang.kau.utils.*
|
||||
import ca.allanwang.kau.utils.fadeIn
|
||||
import ca.allanwang.kau.utils.fadeOut
|
||||
import ca.allanwang.kau.utils.gone
|
||||
import ca.allanwang.kau.utils.goneIf
|
||||
import ca.allanwang.kau.utils.inflate
|
||||
import ca.allanwang.kau.utils.isColorDark
|
||||
import ca.allanwang.kau.utils.isGone
|
||||
import ca.allanwang.kau.utils.isVisible
|
||||
import ca.allanwang.kau.utils.setIcon
|
||||
import ca.allanwang.kau.utils.setMenuIcons
|
||||
import ca.allanwang.kau.utils.visible
|
||||
import ca.allanwang.kau.utils.withMinAlpha
|
||||
import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.utils.L
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.frostDownload
|
||||
import kotlinx.android.synthetic.main.view_video.view.*
|
||||
@ -21,7 +48,9 @@ import kotlinx.android.synthetic.main.view_video.view.*
|
||||
* Created by Allan Wang on 2017-10-13.
|
||||
*/
|
||||
class FrostVideoViewer @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr), FrostVideoViewerContract {
|
||||
|
||||
companion object {
|
||||
@ -51,16 +80,18 @@ class FrostVideoViewer @JvmOverloads constructor(
|
||||
inflate(R.layout.view_video, true)
|
||||
alpha = 0f
|
||||
video_background.setBackgroundColor(
|
||||
if (!Prefs.blackMediaBg && Prefs.bgColor.isColorDark)
|
||||
Prefs.bgColor.withMinAlpha(200)
|
||||
else
|
||||
Color.BLACK)
|
||||
if (!Prefs.blackMediaBg && Prefs.bgColor.isColorDark)
|
||||
Prefs.bgColor.withMinAlpha(200)
|
||||
else
|
||||
Color.BLACK
|
||||
)
|
||||
video.setViewerContract(this)
|
||||
video.pause()
|
||||
video_toolbar.inflateMenu(R.menu.menu_video)
|
||||
context.setMenuIcons(video_toolbar.menu, Prefs.iconColor,
|
||||
R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt,
|
||||
R.id.action_download to GoogleMaterial.Icon.gmd_file_download
|
||||
context.setMenuIcons(
|
||||
video_toolbar.menu, Prefs.iconColor,
|
||||
R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt,
|
||||
R.id.action_download to GoogleMaterial.Icon.gmd_file_download
|
||||
)
|
||||
video_toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
@ -141,7 +172,6 @@ class FrostVideoViewer @JvmOverloads constructor(
|
||||
if (!video_toolbar.isGone)
|
||||
video_toolbar.fadeOut(duration = CONTROL_ANIMATION_DURATION) { video_toolbar.gone() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface FrostVideoViewerContract : VideoControlsVisibilityListener {
|
||||
|
@ -1,10 +1,26 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
|
||||
/**
|
||||
@ -12,21 +28,22 @@ import com.pitchedapps.frost.utils.Prefs
|
||||
*
|
||||
* Basic override to allow us to control swiping
|
||||
*/
|
||||
class FrostViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) {
|
||||
class FrostViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
ViewPager(context, attrs) {
|
||||
var enableSwipe = true
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?) =
|
||||
try {
|
||||
Prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
try {
|
||||
Prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(ev: MotionEvent?): Boolean =
|
||||
try {
|
||||
Prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
try {
|
||||
Prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
false
|
||||
}
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
@ -16,16 +32,22 @@ import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
import com.pitchedapps.frost.fragments.WebFragment
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
import com.pitchedapps.frost.utils.frostDownload
|
||||
import com.pitchedapps.frost.web.*
|
||||
import com.pitchedapps.frost.web.FrostChromeClient
|
||||
import com.pitchedapps.frost.web.FrostJSI
|
||||
import com.pitchedapps.frost.web.FrostWebViewClient
|
||||
import com.pitchedapps.frost.web.NestedWebView
|
||||
import com.pitchedapps.frost.web.shouldUseBasicAgent
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-05-29.
|
||||
*
|
||||
*/
|
||||
class FrostWebView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : NestedWebView(context, attrs, defStyleAttr),
|
||||
FrostContentCore {
|
||||
FrostContentCore {
|
||||
|
||||
override fun reload(animate: Boolean) {
|
||||
if (parent.registerTransition(false, animate))
|
||||
@ -59,7 +81,6 @@ class FrostWebView @JvmOverloads constructor(
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to the main userAgentString to cache it.
|
||||
* This decouples it from the UiThread
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
@ -1,15 +1,31 @@
|
||||
/*
|
||||
* Copyright 2018 Allan Wang
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.pitchedapps.frost.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import ca.allanwang.kau.utils.bindView
|
||||
import ca.allanwang.kau.utils.string
|
||||
import ca.allanwang.kau.utils.tint
|
||||
@ -23,12 +39,13 @@ import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.utils.Prefs
|
||||
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-06-19.
|
||||
*/
|
||||
class Keywords @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
val editText: AppCompatEditText by bindView(R.id.edit_text)
|
||||
@ -51,7 +68,8 @@ class Keywords @JvmOverloads constructor(
|
||||
recycler.layoutManager = LinearLayoutManager(context)
|
||||
recycler.adapter = adapter
|
||||
adapter.withEventHook(object : ClickEventHook<KeywordItem>() {
|
||||
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<KeywordItem>, item: KeywordItem) {
|
||||
adapter.remove(position)
|
||||
@ -62,7 +80,6 @@ class Keywords @JvmOverloads constructor(
|
||||
fun save() {
|
||||
Prefs.notificationKeywords = adapter.adapterItems.mapTo(mutableSetOf()) { it.keyword }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun IIcon.keywordDrawable(context: Context): Drawable = toDrawable(context, 20, Prefs.textColor)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user