1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-10 04:52:38 +01:00

Reorganize observables and clean up

This commit is contained in:
Allan Wang 2017-06-15 20:41:55 -07:00
parent c8f76b5aa4
commit f84a05f8ae
13 changed files with 147 additions and 205 deletions

View File

@ -32,7 +32,6 @@ import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL
import com.pitchedapps.frost.fragments.WebFragment
import com.pitchedapps.frost.services.requestNotifications
import com.pitchedapps.frost.utils.*
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import org.jetbrains.anko.childrenSequence
@ -46,8 +45,12 @@ class MainActivity : BaseActivity() {
val appBar: AppBarLayout by bindView(R.id.appbar)
lateinit var drawer: Drawer
lateinit var drawerHeader: AccountHeader
var titleDisposable: Disposable? = null
var refreshObservable = PublishSubject.create<Boolean>()!!
var webFragmentObservable = PublishSubject.create<Int>()!!
var lastPosition = -1
companion object {
const val FRAGMENT_REFRESH = 99
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -59,7 +62,10 @@ class MainActivity : BaseActivity() {
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
updateTitleListener()
if (lastPosition == position) return
if (lastPosition != -1) webFragmentObservable.onNext(-(lastPosition + 1))
webFragmentObservable.onNext(position)
lastPosition = position
}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
@ -75,13 +81,13 @@ class MainActivity : BaseActivity() {
}
}
})
viewPager.post { webFragmentObservable.onNext(0); lastPosition = 0 } //trigger hook so title is set
setupDrawer(savedInstanceState)
setupTabs()
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
viewPager.post { updateTitleListener() }
theme()
}
@ -100,11 +106,6 @@ class MainActivity : BaseActivity() {
drawer
}
fun updateTitleListener() {
titleDisposable?.dispose()
titleDisposable = currentFragment.web.addTitleListener({ toolbar.title = it })
}
fun setupTabs() {
viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
tabs.addOnTabSelectedListener(object : TabLayout.ViewPagerOnTabSelectedListener(viewPager) {
@ -154,7 +155,7 @@ class MainActivity : BaseActivity() {
else when (profile.identifier) {
-2L -> launchNewTask(LoginActivity::class.java, clearStack = false)
-3L -> launchNewTask(SelectorActivity::class.java, cookies(), false)
else -> switchUser(profile.identifier, { refreshObservable.onNext(true) })
else -> switchUser(profile.identifier, { refreshAll() })
}
false
}
@ -181,7 +182,7 @@ class MainActivity : BaseActivity() {
}
fun refreshAll() {
refreshObservable.onNext(true)
webFragmentObservable.onNext(FRAGMENT_REFRESH)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -197,7 +198,7 @@ class MainActivity : BaseActivity() {
putParcelableArrayListExtra(EXTRA_COOKIES, cookies())
})
}
R.id.action_changelog -> showChangelog(R.xml.changelog)
R.id.action_changelog -> showChangelog(R.xml.changelog, { theme() })
R.id.action_call -> {
requestNotifications(Prefs.userId)
}
@ -218,7 +219,7 @@ class MainActivity : BaseActivity() {
inner class SectionsPagerAdapter(fm: FragmentManager, val pages: List<FbTab>) : FragmentPagerAdapter(fm) {
override fun getItem(position: Int) = WebFragment(pages[position])
override fun getItem(position: Int) = WebFragment(pages[position], position)
override fun getCount() = pages.size

View File

@ -9,6 +9,7 @@ import com.jude.swipbackhelper.SwipeBackHelper
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.url
import com.pitchedapps.frost.web.FrostWebView
import io.reactivex.disposables.Disposable
/**

View File

@ -8,9 +8,9 @@ import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic
import com.pitchedapps.frost.R
import com.pitchedapps.frost.web.FrostWebViewClient
import com.pitchedapps.frost.web.FrostWebViewClientMenu
import io.reactivex.subjects.BehaviorSubject
import com.pitchedapps.frost.web.FrostWebViewCore
enum class FbTab(@StringRes val titleId: Int, val icon: IIcon, relativeUrl: String, val webClient: ((refreshObservable: BehaviorSubject<Boolean>) -> FrostWebViewClient)? = null) {
enum class FbTab(@StringRes val titleId: Int, val icon: IIcon, relativeUrl: String, val webClient: ((webCore: FrostWebViewCore) -> FrostWebViewClient)? = null) {
FEED(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, ""),
FEED_MOST_RECENT(R.string.most_recent, GoogleMaterial.Icon.gmd_grade, "/?sk=h_chr"),
FEED_TOP_STORIES(R.string.top_stories, GoogleMaterial.Icon.gmd_star, "/?sk=h_nor"),

View File

@ -1,6 +1,5 @@
package com.pitchedapps.frost.fragments
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
@ -25,29 +24,23 @@ class WebFragment : Fragment() {
companion object {
private const val ARG_URL = "arg_url"
private const val ARG_URL_ENUM = "arg_url_enum"
operator fun invoke(url: String) = WebFragment().withBundle {
putString(ARG_URL, url)
}
private const val ARG_POSITION = "arg_position"
operator fun invoke(data: FbTab) = WebFragment().withBundle {
operator fun invoke(data: FbTab, position: Int) = WebFragment().withBundle {
putString(ARG_URL, data.url)
putInt(ARG_POSITION, position)
putSerializable(ARG_URL_ENUM, data)
}
}
// val refresh: SwipeRefreshLayout by lazy { frostWebView.refresh }
val web: FrostWebViewCore by lazy { frostWebView.web }
lateinit var url: String
var urlEnum: FbTab? = null
val url: String by lazy { arguments.getString(ARG_URL) }
val urlEnum: FbTab by lazy { arguments.getSerializable(ARG_URL_ENUM) as FbTab }
val position: Int by lazy { arguments.getInt(ARG_POSITION) }
lateinit private var frostWebView: FrostWebView
private var firstLoad = true
private var refreshDisposable: Disposable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
url = arguments.getString(ARG_URL)
urlEnum = arguments.getSerializable(ARG_URL_ENUM) as? FbTab
}
private var activityDisposable: Disposable? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
@ -75,37 +68,45 @@ class WebFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
refreshDisposable?.dispose()
if (context is MainActivity)
refreshDisposable = context.refreshObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
clearHistory ->
if (clearHistory) web.clearHistory()
web.loadBaseUrl()
activityDisposable?.dispose()
if (context is MainActivity) {
activityDisposable = context.webFragmentObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
/**
* Execute actions based on flags
* Flags between -10 and 10 are reserved for viewpager events
*/
when (it) {
MainActivity.FRAGMENT_REFRESH -> {
web.clearHistory()
web.loadBaseUrl(true)
}
position -> {
context.toolbar.setTitle(urlEnum.titleId)
pauseLoad = false
}
-(position + 1) -> { //we are moving away from this fragment
pauseLoad = true
}
}
}
}
}
override fun onDetach() {
refreshDisposable?.dispose()
activityDisposable?.dispose()
super.onDetach()
}
@SuppressLint("SetJavaScriptEnabled")
override fun onResume() {
super.onResume()
pauseLoad = false
firstLoad()
}
override fun onPause() {
pauseLoad = true
super.onPause()
}
var pauseLoad: Boolean
get() = web.settings.blockNetworkLoads
set(value) {
if (urlEnum != FbTab.MENU)
web.settings.blockNetworkLoads = value
web.settings.blockNetworkLoads = value
}

View File

@ -1,69 +0,0 @@
package com.pitchedapps.frost.utils
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.support.v4.app.ActivityOptionsCompat
import android.support.v4.content.ContextCompat
import ca.allanwang.kau.utils.adjustAlpha
import ca.allanwang.kau.utils.isColorDark
import ca.allanwang.kau.utils.lighten
import ca.allanwang.kau.utils.startActivity
import com.afollestad.materialdialogs.MaterialDialog
import com.pitchedapps.frost.LoginActivity
import com.pitchedapps.frost.R
import com.pitchedapps.frost.WebOverlayActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbTab
/**
* Created by Allan Wang on 2017-06-03.
*/
internal const val EXTRA_COOKIES = "extra_cookies"
internal const val ARG_URL = "arg_url"
fun Context.launchNewTask(clazz: Class<out Activity>, cookieList: ArrayList<CookieModel> = arrayListOf(), clearStack: Boolean = clazz != LoginActivity::class.java) {
startActivity(clazz, clearStack, {
putParcelableArrayListExtra(EXTRA_COOKIES, cookieList)
})
}
fun Activity.cookies(): ArrayList<CookieModel> {
return intent?.extras?.getParcelableArrayList<CookieModel>(EXTRA_COOKIES) ?: arrayListOf()
}
fun Context.launchWebOverlay(url: String) {
val intent = Intent(this, WebOverlayActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra(ARG_URL, url)
val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.slide_in_right, R.anim.slide_out_right).toBundle()
ContextCompat.startActivity(this, intent, bundle)
}
fun WebOverlayActivity.url(): String {
return intent.extras?.getString(ARG_URL) ?: FbTab.FEED.url
}
fun Activity.materialDialogThemed(action: MaterialDialog.Builder.() -> Unit): MaterialDialog {
val builder = MaterialDialog.Builder(this)
val dimmerTextColor = Prefs.textColor.adjustAlpha(0.8f)
builder.titleColor(Prefs.textColor)
.contentColor(dimmerTextColor)
.widgetColor(dimmerTextColor)
.backgroundColor(Prefs.bgColor.lighten(0.1f))
.positiveColor(Prefs.textColor)
.negativeColor(Prefs.textColor)
.neutralColor(Prefs.textColor)
builder.action()
return builder.show()
}
fun Activity.setFrostTheme() {
val isTransparent = Color.alpha(Prefs.bgColor) != 255
if (Prefs.bgColor.isColorDark())
setTheme(if (isTransparent) R.style.FrostTheme_Transparent else R.style.FrostTheme)
else
setTheme(if (isTransparent) R.style.FrostTheme_Light_Transparent else R.style.FrostTheme_Light)
}

View File

@ -1,51 +0,0 @@
package com.pitchedapps.frost.utils
import java.io.Serializable
import kotlin.reflect.KProperty
/**
* Created by Allan Wang on 2017-05-30.
*
* Lazy delegate that can be invalidated if needed
* https://stackoverflow.com/a/37294840/4407321
*/
private object UNINITIALIZED
fun <T : Any> lazyResettable(initializer: () -> T): LazyResettable<T> = LazyResettable<T>(initializer)
class LazyResettable<T : Any>(private val initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
@Volatile private var _value: Any = UNINITIALIZED
private val lock = lock ?: this
fun invalidate() {
_value = UNINITIALIZED
}
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED)
@Suppress("UNCHECKED_CAST")
return _v1 as T
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED) {
@Suppress("UNCHECKED_CAST")
_v2 as T
} else {
val typedValue = initializer()
_value = typedValue
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
operator fun setValue(any: Any, property: KProperty<*>, t: T) {
_value = t
}
}

View File

@ -3,6 +3,7 @@ package com.pitchedapps.frost.utils
import android.graphics.Color
import ca.allanwang.kau.kpref.KPref
import ca.allanwang.kau.kpref.kpref
import ca.allanwang.kau.utils.lazyResettable
import com.pitchedapps.frost.injectors.InjectorContract
/**

View File

@ -1,18 +1,70 @@
package com.pitchedapps.frost.utils
import android.app.Activity
import android.content.Context
import android.net.ConnectivityManager
import android.content.Intent
import android.graphics.Color
import android.support.v4.app.ActivityOptionsCompat
import android.support.v4.content.ContextCompat
import ca.allanwang.kau.utils.*
import com.afollestad.materialdialogs.MaterialDialog
import com.pitchedapps.frost.LoginActivity
import com.pitchedapps.frost.R
import com.pitchedapps.frost.WebOverlayActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbTab
/**
* Created by Allan Wang on 2017-05-28.
* Created by Allan Wang on 2017-06-03.
*/
object Utils {
fun dpToPx(dp: Int) = (dp * android.content.res.Resources.getSystem().displayMetrics.density).toInt()
fun pxToDp(px:Int) = (px / android.content.res.Resources.getSystem().displayMetrics.density).toInt()
internal const val EXTRA_COOKIES = "extra_cookies"
internal const val ARG_URL = "arg_url"
fun isNetworkAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = connectivityManager.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting
}
fun Context.launchNewTask(clazz: Class<out Activity>, cookieList: ArrayList<CookieModel> = arrayListOf(), clearStack: Boolean = clazz != LoginActivity::class.java) {
startActivity(clazz, clearStack, {
putParcelableArrayListExtra(EXTRA_COOKIES, cookieList)
})
}
fun Activity.cookies(): ArrayList<CookieModel> {
return intent?.extras?.getParcelableArrayList<CookieModel>(EXTRA_COOKIES) ?: arrayListOf()
}
fun Context.launchWebOverlay(url: String) {
val intent = Intent(this, WebOverlayActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
intent.putExtra(ARG_URL, url)
val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.slide_in_right, R.anim.slide_out_right).toBundle()
ContextCompat.startActivity(this, intent, bundle)
}
fun WebOverlayActivity.url(): String {
return intent.extras?.getString(ARG_URL) ?: FbTab.FEED.url
}
fun Activity.materialDialogThemed(action: MaterialDialog.Builder.() -> Unit): MaterialDialog {
val builder = MaterialDialog.Builder(this).theme()
builder.action()
return builder.show()
}
fun MaterialDialog.Builder.theme(): MaterialDialog.Builder {
val dimmerTextColor = Prefs.textColor.adjustAlpha(0.8f)
titleColor(Prefs.textColor)
contentColor(dimmerTextColor)
widgetColor(dimmerTextColor)
backgroundColor(Prefs.bgColor.lighten(0.1f).withMinAlpha(200))
positiveColor(Prefs.textColor)
negativeColor(Prefs.textColor)
neutralColor(Prefs.textColor)
return this
}
fun Activity.setFrostTheme() {
val isTransparent = Color.alpha(Prefs.bgColor) != 255
if (Prefs.bgColor.isColorDark())
setTheme(if (isTransparent) R.style.FrostTheme_Transparent else R.style.FrostTheme)
else
setTheme(if (isTransparent) R.style.FrostTheme_Light_Transparent else R.style.FrostTheme_Light)
}

View File

@ -10,7 +10,10 @@ import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
class FrostChromeClient(val progressObservable: Subject<Int>, val titleObservable: BehaviorSubject<String>) : WebChromeClient() {
class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() {
val progressObservable: Subject<Int> = webCore.progressObservable
val titleObservable: BehaviorSubject<String> = webCore.titleObservable
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
L.i("Chrome Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}")

View File

@ -22,12 +22,14 @@ import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
open class FrostWebViewClient(val refreshObservable: Subject<Boolean>) : WebViewClient() {
open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() {
val refreshObservable: Subject<Boolean> = webCore.refreshObservable
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
L.i("FWV Loading $url")
L.v("Cookies ${CookieManager.getInstance().getCookie(url)}")
// L.v("Cookies ${CookieManager.getInstance().getCookie(url)}")
refreshObservable.onNext(true)
if (!url.contains(FACEBOOK_COM)) return
if (url.contains("logout.php")) FbCookie.logout(Prefs.userId, { launchLogin(view.context) })
@ -49,16 +51,16 @@ open class FrostWebViewClient(val refreshObservable: Subject<Boolean>) : WebView
}
L.i("Page finished $url")
JsActions.LOGIN_CHECK.inject(view)
onPageFinishedActions(view as FrostWebViewCore, url)
onPageFinishedActions(url)
}
open internal fun onPageFinishedActions(view: FrostWebViewCore, url: String?) {
onPageFinishedActions(view)
open internal fun onPageFinishedActions(url: String?) {
injectAndFinish()
}
internal fun onPageFinishedActions(view: FrostWebViewCore) {
internal fun injectAndFinish() {
L.d("Page finished reveal")
view.jsInject(CssHider.HEADER,
webCore.jsInject(CssHider.HEADER,
Prefs.themeInjector,
JsAssets.CLICK_INTERCEPTOR,
callback = {

View File

@ -9,10 +9,10 @@ import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
class FrostWebViewClientMenu(refreshObservable: Subject<Boolean>) : FrostWebViewClient(refreshObservable) {
class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(webCore) {
var content: String? = null
var view: FrostWebViewCore? = null
val progressObservable: Subject<Int> = webCore.progressObservable
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
@ -31,32 +31,30 @@ class FrostWebViewClientMenu(refreshObservable: Subject<Boolean>) : FrostWebView
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
with(view as FrostWebViewCore) {
if (url == view.baseUrl) {
this@FrostWebViewClientMenu.view = view
inject(JsAssets.MENU, view, {
inject(JsAssets.MENU_CLICK, view) //menu injection must be after or we will have a loop from the click listener
})
} else {
inject(JsAssets.MENU_CLICK, view)
}
if (url == webCore.baseUrl) {
progressObservable.onNext(99)
inject(JsAssets.MENU, webCore, {
inject(JsAssets.MENU_CLICK, webCore) //menu injection must be after or we will have a loop from the click listener
})
} else {
inject(JsAssets.MENU_CLICK, webCore)
}
}
override fun emit(flag: Int) {
super.emit(flag)
if (view != null) super.onPageFinishedActions(view!!)
view = null
progressObservable.onNext(100)
super.injectAndFinish()
}
override fun onPageFinishedActions(view: FrostWebViewCore, url: String?) {
override fun onPageFinishedActions(url: String?) {
when (url) {
"https://m.facebook.com/settings",
"https://m.facebook.com/settings#",
"https://m.facebook.com/settings#!/settings?soft=bookmarks" -> {
//do nothing; we will further inject before revealing
}
else -> super.onPageFinishedActions(view)
else -> injectAndFinish()
}
}

View File

@ -18,7 +18,6 @@ import ca.allanwang.kau.utils.fadeOut
import ca.allanwang.kau.utils.isVisible
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
import com.pitchedapps.frost.utils.L
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
@ -27,9 +26,6 @@ import io.reactivex.subjects.BehaviorSubject
/**
* Created by Allan Wang on 2017-05-29.
*
* Courtesy of takahirom
*
* https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
*/
class FrostWebViewCore @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
@ -63,9 +59,9 @@ class FrostWebViewCore @JvmOverloads constructor(
settings.userAgentString = USER_AGENT_BASIC
// settings.domStorageEnabled = true
setLayerType(View.LAYER_TYPE_HARDWARE, null)
frostWebClient = baseEnum?.webClient?.invoke(refreshObservable) ?: FrostWebViewClient(refreshObservable)
frostWebClient = baseEnum?.webClient?.invoke(this) ?: FrostWebViewClient(this)
webViewClient = frostWebClient
webChromeClient = FrostChromeClient(progressObservable, titleObservable)
webChromeClient = FrostChromeClient(this)
addJavascriptInterface(FrostJSI(context, this), "Frost")
setBackgroundColor(Color.TRANSPARENT)
}
@ -84,6 +80,7 @@ class FrostWebViewCore @JvmOverloads constructor(
/**
* Hook onto the refresh observable for one cycle
* Note that this is a behaviour subject so the first 'false' emission should be ignored
* Animate toggles between the fancy ripple and the basic fade
*/
fun registerTransition(animate: Boolean) {
var dispose: Disposable? = null
@ -107,6 +104,12 @@ class FrostWebViewCore @JvmOverloads constructor(
fun addTitleListener(subscriber: (title: String) -> Unit, scheduler: Scheduler = AndroidSchedulers.mainThread()): Disposable
= titleObservable.observeOn(scheduler).subscribe(subscriber)
/**
* Handle nested scrolling against SwipeRecyclerView
* Courtesy of takahirom
*
* https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
*/
override fun onTouchEvent(ev: MotionEvent): Boolean {
val event = MotionEvent.obtain(ev)
val action = event.action

View File

@ -20,7 +20,7 @@ VERSION_CODE=1
VERSION_NAME=0.1
ANDROID_SUPPORT_LIBS=26.0.0-alpha1
KAU=5aba646c13
KAU=15e7796296
MATERIAL_DRAWER=5.9.2
MATERIAL_DRAWER_KT=1.0.2
IICON_GOOGLE=3.0.1.0