diff options
author | Allan Wang <me@allanwang.ca> | 2017-12-21 02:16:34 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-21 02:16:34 -0500 |
commit | d683cae6ffe644a9f63eea6cf3b7e59d2bde617b (patch) | |
tree | 517fe1d44c27084ccd87507d9804ba28f15c1647 /app/src/main/kotlin/com/pitchedapps/frost/activities | |
parent | 82f9aca96493316bc62008f2b3167d34a6029b38 (diff) | |
download | frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.gz frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.bz2 frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.zip |
Enhancement/fragment interface (#564)
* Begin fragment interfaces and themable contracts
* Prepare swiperefresh interface
* Snapshot
* Add compilable version
* Revamp once more
* Finalize layouts
* Cleanup
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/activities')
4 files changed, 464 insertions, 433 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt new file mode 100644 index 00000000..389ff88e --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseMainActivity.kt @@ -0,0 +1,402 @@ +package com.pitchedapps.frost.activities + +import android.annotation.SuppressLint +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.graphics.PointF +import android.graphics.drawable.ColorDrawable +import android.net.Uri +import android.os.Bundle +import android.support.annotation.StringRes +import android.support.design.widget.AppBarLayout +import android.support.design.widget.CoordinatorLayout +import android.support.design.widget.FloatingActionButton +import android.support.design.widget.TabLayout +import android.support.v4.app.ActivityOptionsCompat +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentPagerAdapter +import android.support.v7.widget.Toolbar +import android.view.Menu +import android.view.MenuItem +import android.webkit.ValueCallback +import android.webkit.WebChromeClient +import android.widget.FrameLayout +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 co.zsmb.materialdrawerkt.builders.Builder +import co.zsmb.materialdrawerkt.builders.accountHeader +import co.zsmb.materialdrawerkt.builders.drawer +import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem +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.crashlytics.android.answers.ContentViewEvent +import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.materialdrawer.AccountHeader +import com.mikepenz.materialdrawer.Drawer +import com.pitchedapps.frost.BuildConfig +import com.pitchedapps.frost.R +import com.pitchedapps.frost.contracts.FileChooserContract +import com.pitchedapps.frost.contracts.FileChooserDelegate +import com.pitchedapps.frost.contracts.MainActivityContract +import com.pitchedapps.frost.contracts.VideoViewHolder +import com.pitchedapps.frost.dbflow.TAB_COUNT +import com.pitchedapps.frost.dbflow.loadFbCookie +import com.pitchedapps.frost.dbflow.loadFbTabs +import com.pitchedapps.frost.enums.MainActivityLayout +import com.pitchedapps.frost.enums.Theme +import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.facebook.FbItem +import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL +import com.pitchedapps.frost.fragments.BaseFragment +import com.pitchedapps.frost.parsers.SearchParser +import com.pitchedapps.frost.utils.* +import com.pitchedapps.frost.utils.iab.FrostBilling +import com.pitchedapps.frost.utils.iab.IS_FROST_PRO +import com.pitchedapps.frost.utils.iab.IabMain +import com.pitchedapps.frost.views.BadgedIcon +import com.pitchedapps.frost.views.FrostVideoViewer +import com.pitchedapps.frost.views.FrostViewPager +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread + +/** + * Created by Allan Wang on 20/12/17. + * + * Most of the logic that is unrelated to handling fragments + */ +abstract class BaseMainActivity : BaseActivity(), MainActivityContract, + FileChooserContract by FileChooserDelegate(), + VideoViewHolder, SearchViewHolder, + FrostBilling by IabMain() { + + lateinit var adapter: SectionsPagerAdapter + override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper) + val toolbar: Toolbar by bindView(R.id.toolbar) + val viewPager: FrostViewPager by bindView(R.id.container) + val fab: FloatingActionButton by bindView(R.id.fab) + val tabs: TabLayout by bindView(R.id.tabs) + val appBar: AppBarLayout by bindView(R.id.appbar) + val coordinator: CoordinatorLayout by bindView(R.id.main_content) + override var videoViewer: FrostVideoViewer? = null + lateinit var drawer: Drawer + lateinit var drawerHeader: AccountHeader + + override var searchView: SearchView? = null + private val searchViewCache = mutableMapOf<String, List<SearchItem>>() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (BuildConfig.VERSION_CODE > Prefs.versionCode) { + Prefs.versionCode = BuildConfig.VERSION_CODE + if (!BuildConfig.DEBUG) { + frostChangelog() + frostAnswersCustom("Version", + "Version code" to BuildConfig.VERSION_CODE, + "Version name" to BuildConfig.VERSION_NAME, + "Build type" to BuildConfig.BUILD_TYPE, + "Frost id" to Prefs.frostId) + } + } + setFrameContentView(Prefs.mainActivityLayout.layoutRes) + setSupportActionBar(toolbar) + adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs()) + viewPager.adapter = adapter + viewPager.offscreenPageLimit = TAB_COUNT + setupDrawer(savedInstanceState) + +// fab.setOnClickListener { view -> +// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) +// .setAction("Action", null).show() +// } + setFrostColors(toolbar, themeWindow = false, headers = arrayOf(appBar), backgrounds = arrayOf(viewPager)) + tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor()) + onCreateBilling() + } + + fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) { + (0 until tabs.tabCount).asSequence().forEach { i -> + action(i, tabs.getTabAt(i)!!.customView as BadgedIcon) + } + } + + private fun setupDrawer(savedInstanceState: Bundle?) { + val navBg = Prefs.bgColor.withMinAlpha(200).toLong() + val navHeader = Prefs.headerColor.withMinAlpha(200) + drawer = drawer { + toolbar = this@BaseMainActivity.toolbar + savedInstance = savedInstanceState + translucentStatusBar = false + sliderBackgroundColor = navBg + drawerHeader = accountHeader { + customViewRes = R.layout.material_drawer_header + textColor = Prefs.iconColor.toLong() + backgroundDrawable = ColorDrawable(navHeader) + selectionSecondLineShown = false + cookies().forEach { (id, name) -> + profile(name = name ?: "") { + iconUrl = PROFILE_PICTURE_URL(id) + textColor = Prefs.textColor.toLong() + selectedTextColor = Prefs.textColor.toLong() + selectedColor = 0x00000001.toLong() + identifier = id + } + } + profileSetting(nameRes = R.string.kau_logout) { + iicon = GoogleMaterial.Icon.gmd_exit_to_app + iconColor = Prefs.textColor.toLong() + textColor = Prefs.textColor.toLong() + identifier = -2L + } + profileSetting(nameRes = R.string.kau_add_account) { + iconDrawable = IconicsDrawable(this@BaseMainActivity, GoogleMaterial.Icon.gmd_add).actionBar().paddingDp(5).color(Prefs.textColor) + textColor = Prefs.textColor.toLong() + identifier = -3L + } + profileSetting(nameRes = R.string.kau_manage_account) { + iicon = GoogleMaterial.Icon.gmd_settings + iconColor = Prefs.textColor.toLong() + textColor = Prefs.textColor.toLong() + identifier = -4L + } + onProfileChanged { _, profile, current -> + if (current) launchWebOverlay(FbItem.PROFILE.url) + else when (profile.identifier) { + -2L -> { + val currentCookie = loadFbCookie(Prefs.userId) + if (currentCookie == null) { + toast(R.string.account_not_found) + FbCookie.reset { launchLogin(cookies(), true) } + } else { + materialDialogThemed { + title(R.string.kau_logout) + 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) } + } + } + } + -3L -> launchNewTask(LoginActivity::class.java, clearStack = false) + -4L -> launchNewTask(SelectorActivity::class.java, cookies(), false) + else -> { + FbCookie.switchUser(profile.identifier, { refreshAll() }) + tabsForEachView { _, view -> view.badgeText = null } + } + } + false + } + } + drawerHeader.setActiveProfile(Prefs.userId) + primaryFrostItem(FbItem.FEED_MOST_RECENT) + primaryFrostItem(FbItem.FEED_TOP_STORIES) + primaryFrostItem(FbItem.ACTIVITY_LOG) + divider() + primaryFrostItem(FbItem.PHOTOS) + primaryFrostItem(FbItem.GROUPS) + primaryFrostItem(FbItem.FRIENDS) + primaryFrostItem(FbItem.CHAT) + primaryFrostItem(FbItem.PAGES) + divider() + primaryFrostItem(FbItem.EVENTS) + primaryFrostItem(FbItem.BIRTHDAYS) + primaryFrostItem(FbItem.ON_THIS_DAY) + divider() + primaryFrostItem(FbItem.NOTES) + primaryFrostItem(FbItem.SAVED) + } + } + + private fun Builder.primaryFrostItem(item: FbItem) = this.primaryItem(item.titleId) { + iicon = item.icon + iconColor = Prefs.textColor.toLong() + textColor = Prefs.textColor.toLong() + selectedIconColor = Prefs.textColor.toLong() + selectedTextColor = Prefs.textColor.toLong() + selectedColor = 0x00000001.toLong() + identifier = item.titleId.toLong() + onClick { _ -> + frostAnswers { + logContentView(ContentViewEvent() + .putContentName(item.name) + .putContentType("drawer_item")) + } + launchWebOverlay(item.url) + false + } + } + + private fun Builder.secondaryFrostItem(@StringRes title: Int, onClick: () -> Unit) = this.secondaryItem(title) { + textColor = Prefs.textColor.toLong() + selectedIconColor = Prefs.textColor.toLong() + selectedTextColor = Prefs.textColor.toLong() + selectedColor = 0x00000001.toLong() + identifier = title.toLong() + onClick { _ -> onClick(); false } + } + + fun refreshAll() { + fragmentSubject.onNext(REQUEST_REFRESH) + } + + 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) + searchViewBindIfNull { + bindSearchView(menu, R.id.action_search, Prefs.iconColor) { + textCallback = { query, _ -> + val results = searchViewCache[query] + if (results != null) + runOnUiThread { searchView?.results = results } + else + doAsync { + val data = SearchParser.query(query) ?: return@doAsync + val items = data.map { SearchItem(it.href, it.title, it.description) }.toMutableList() + if (items.isNotEmpty()) + items.add(SearchItem("${FbItem._SEARCH.url}?q=$query", string(R.string.show_all_results), iicon = null)) + searchViewCache.put(query, items) + uiThread { searchView?.results = items } + } + } + textDebounceInterval = 300 + searchCallback = { query, _ -> launchWebOverlay("${FbItem._SEARCH.url}/?q=$query"); true } + closeListener = { _ -> searchViewCache.clear() } + foregroundColor = Prefs.textColor + backgroundColor = Prefs.bgColor.withMinAlpha(200) + onItemClick = { _, key, _, _ -> launchWebOverlay(key) } + } + } + return true + } + + @SuppressLint("RestrictedApi") + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_settings -> { + val intent = Intent(this, SettingsActivity::class.java) + intent.putParcelableArrayListExtra(EXTRA_COOKIES, cookies()) + val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.kau_slide_in_right, R.anim.kau_fade_out).toBundle() + startActivityForResult(intent, ACTIVITY_SETTINGS, bundle) + } + else -> return super.onOptionsItemSelected(item) + } + return true + } + + override fun openFileChooser(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams) { + openMediaPicker(filePathCallback, fileChooserParams) + } + + @SuppressLint("NewApi") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (onActivityResultWeb(requestCode, resultCode, data)) return + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == ACTIVITY_SETTINGS) { + if (resultCode and REQUEST_RESTART_APPLICATION > 0) { //completely restart application + L.d("Restart Application Requested") + val intent = packageManager.getLaunchIntentForPackage(packageName) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + val pending = PendingIntent.getActivity(this, 666, intent, PendingIntent.FLAG_CANCEL_CURRENT) + val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager + if (buildIsMarshmallowAndUp) + alarm.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) + else + alarm.setExact(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) + finish() + System.exit(0) + return + } + if (resultCode and REQUEST_RESTART > 0) return restart() + /* + * These results can be stacked + */ + if (resultCode and REQUEST_REFRESH > 0) fragmentSubject.onNext(REQUEST_REFRESH) + if (resultCode and REQUEST_NAV > 0) frostNavigationBar() + if (resultCode and REQUEST_TEXT_ZOOM > 0) fragmentSubject.onNext(REQUEST_TEXT_ZOOM) + if (resultCode and REQUEST_SEARCH > 0) invalidateOptionsMenu() + } + } + + override fun onResume() { + super.onResume() + FbCookie.switchBackUser { } + } + + override fun onStart() { + //validate some pro features + if (!IS_FROST_PRO) { + if (Prefs.theme == Theme.CUSTOM.ordinal) Prefs.theme = Theme.DEFAULT.ordinal + } + super.onStart() + } + + override fun onDestroy() { + onDestroyBilling() + super.onDestroy() + } + + override fun backConsumer(): Boolean { + if (currentFragment.onBackPressed()) return true + if (Prefs.exitConfirmation) { + materialDialogThemed { + title(R.string.kau_exit) + content(R.string.kau_exit_confirmation) + positiveText(R.string.kau_yes) + negativeText(R.string.kau_no) + onPositive { _, _ -> finish() } + checkBoxPromptRes(R.string.kau_do_not_show_again, false, { _, b -> Prefs.exitConfirmation = !b }) + } + return true + } + return false + } + + inline val currentFragment + get() = supportFragmentManager.findFragmentByTag("android:switcher:${R.id.container}:${viewPager.currentItem}") as BaseFragment + + inner class SectionsPagerAdapter(fm: FragmentManager, val pages: List<FbItem>) : FragmentPagerAdapter(fm) { + + override fun getItem(position: Int): Fragment { + val item = pages[position] + val fragment = BaseFragment(item.fragmentCreator, item, position) + //If first load hasn't occurred, add a listener + // todo check +// if (!firstLoadFinished) { +// var disposable: Disposable? = null +// fragment.post { +// disposable = it.web.refreshObservable.subscribe { +// if (!it) { +// //Ensure first load finisher only happens once +// if (!firstLoadFinished) firstLoadFinished = true +// disposable?.dispose() +// disposable = null +// } +// } +// } +// } + return fragment + } + + override fun getCount() = pages.size + + override fun getPageTitle(position: Int): CharSequence = getString(pages[position].titleId) + } + + override val lowerVideoPadding: PointF + get() = + if (Prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR) + PointF(0f, toolbar.height.toFloat()) + else + PointF(0f, 0f) +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt index 1ba7f4c3..f72807d1 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt @@ -1,98 +1,20 @@ package com.pitchedapps.frost.activities -import android.annotation.SuppressLint -import android.app.AlarmManager -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.graphics.PointF -import android.graphics.drawable.ColorDrawable -import android.net.Uri import android.os.Bundle -import android.support.annotation.StringRes -import android.support.design.widget.AppBarLayout -import android.support.design.widget.CoordinatorLayout -import android.support.design.widget.FloatingActionButton import android.support.design.widget.TabLayout -import android.support.v4.app.ActivityOptionsCompat -import android.support.v4.app.Fragment -import android.support.v4.app.FragmentManager -import android.support.v4.app.FragmentPagerAdapter import android.support.v4.view.ViewPager -import android.support.v7.widget.Toolbar -import android.view.Menu -import android.view.MenuItem -import android.webkit.ValueCallback -import android.webkit.WebChromeClient -import android.widget.FrameLayout -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 co.zsmb.materialdrawerkt.builders.Builder -import co.zsmb.materialdrawerkt.builders.accountHeader -import co.zsmb.materialdrawerkt.builders.drawer -import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem -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.crashlytics.android.answers.ContentViewEvent -import com.mikepenz.google_material_typeface_library.GoogleMaterial -import com.mikepenz.iconics.IconicsDrawable -import com.mikepenz.materialdrawer.AccountHeader -import com.mikepenz.materialdrawer.Drawer -import com.pitchedapps.frost.BuildConfig -import com.pitchedapps.frost.R -import com.pitchedapps.frost.contracts.ActivityWebContract -import com.pitchedapps.frost.contracts.FileChooserContract -import com.pitchedapps.frost.contracts.FileChooserDelegate -import com.pitchedapps.frost.contracts.VideoViewHolder -import com.pitchedapps.frost.dbflow.TAB_COUNT -import com.pitchedapps.frost.dbflow.loadFbCookie -import com.pitchedapps.frost.dbflow.loadFbTabs -import com.pitchedapps.frost.enums.MainActivityLayout -import com.pitchedapps.frost.enums.Theme -import com.pitchedapps.frost.facebook.FbCookie -import com.pitchedapps.frost.facebook.FbCookie.switchUser import com.pitchedapps.frost.facebook.FbItem -import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL -import com.pitchedapps.frost.fragments.WebFragment -import com.pitchedapps.frost.parsers.SearchParser -import com.pitchedapps.frost.utils.* -import com.pitchedapps.frost.utils.iab.FrostBilling -import com.pitchedapps.frost.utils.iab.IS_FROST_PRO -import com.pitchedapps.frost.utils.iab.IabMain +import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.views.BadgedIcon -import com.pitchedapps.frost.views.FrostVideoViewer -import com.pitchedapps.frost.views.FrostViewPager import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread import org.jsoup.Jsoup import java.util.concurrent.TimeUnit -class MainActivity : BaseActivity(), - ActivityWebContract, FileChooserContract by FileChooserDelegate(), - VideoViewHolder, SearchViewHolder, - FrostBilling by IabMain() { +class MainActivity : BaseMainActivity() { - lateinit var adapter: SectionsPagerAdapter - override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper) - val toolbar: Toolbar by bindView(R.id.toolbar) - val viewPager: FrostViewPager by bindView(R.id.container) - val fab: FloatingActionButton by bindView(R.id.fab) - val tabs: TabLayout by bindView(R.id.tabs) - val appBar: AppBarLayout by bindView(R.id.appbar) - val coordinator: CoordinatorLayout by bindView(R.id.main_content) - override var videoViewer: FrostVideoViewer? = null - lateinit var drawer: Drawer - lateinit var drawerHeader: AccountHeader - var webFragmentObservable = PublishSubject.create<Int>()!! + override val fragmentSubject = PublishSubject.create<Int>()!! var lastPosition = -1 val headerBadgeObservable = PublishSubject.create<String>() var firstLoadFinished = false @@ -101,47 +23,20 @@ class MainActivity : BaseActivity(), L.i("First fragment load has finished") field = value } - override var searchView: SearchView? = null - private val searchViewCache = mutableMapOf<String, List<SearchItem>>() - - companion object { - const val ACTIVITY_SETTINGS = 97 - /* - * Possible responses from the SettingsActivity - * after the configurations have changed - */ - const val REQUEST_RESTART_APPLICATION = 1 shl 1 - const val REQUEST_RESTART = 1 shl 2 - const val REQUEST_REFRESH = 1 shl 3 - const val REQUEST_WEB_ZOOM = 1 shl 4 - const val REQUEST_NAV = 1 shl 5 - const val REQUEST_SEARCH = 1 shl 6 - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (BuildConfig.VERSION_CODE > Prefs.versionCode) { - Prefs.versionCode = BuildConfig.VERSION_CODE - if (!BuildConfig.DEBUG) { - frostChangelog() - frostAnswersCustom("Version", - "Version code" to BuildConfig.VERSION_CODE, - "Version name" to BuildConfig.VERSION_NAME, - "Build type" to BuildConfig.BUILD_TYPE, - "Frost id" to Prefs.frostId) - } - } - setFrameContentView(Prefs.mainActivityLayout.layoutRes) - setSupportActionBar(toolbar) - adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs()) - viewPager.adapter = adapter - viewPager.offscreenPageLimit = TAB_COUNT + setupViewPager() + setupTabs() + } + + private fun setupViewPager() { viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { super.onPageSelected(position) if (lastPosition == position) return - if (lastPosition != -1) webFragmentObservable.onNext(-(lastPosition + 1)) - webFragmentObservable.onNext(position) + if (lastPosition != -1) fragmentSubject.onNext(-(lastPosition + 1)) + fragmentSubject.onNext(position) lastPosition = position } @@ -157,30 +52,17 @@ 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() -// } - setFrostColors(toolbar, themeWindow = false, headers = arrayOf(appBar), backgrounds = arrayOf(viewPager)) - tabs.setBackgroundColor(Prefs.mainActivityLayout.backgroundColor()) - onCreateBilling() - } + viewPager.post { fragmentSubject.onNext(0); lastPosition = 0 } //trigger hook so title is set - fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) { - (0 until tabs.tabCount).asSequence().forEach { i -> - action(i, tabs.getTabAt(i)!!.customView as BadgedIcon) - } } - fun setupTabs() { + + private fun setupTabs() { viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs)) tabs.addOnTabSelectedListener(object : TabLayout.ViewPagerOnTabSelectedListener(viewPager) { override fun onTabReselected(tab: TabLayout.Tab) { super.onTabReselected(tab) - currentFragment.web.scrollOrRefresh() + currentFragment.onTabClick() } override fun onTabSelected(tab: TabLayout.Tab) { @@ -215,274 +97,4 @@ class MainActivity : BaseActivity(), } } - fun setupDrawer(savedInstanceState: Bundle?) { - val navBg = Prefs.bgColor.withMinAlpha(200).toLong() - val navHeader = Prefs.headerColor.withMinAlpha(200) - drawer = drawer { - toolbar = this@MainActivity.toolbar - savedInstance = savedInstanceState - translucentStatusBar = false - sliderBackgroundColor = navBg - drawerHeader = accountHeader { - customViewRes = R.layout.material_drawer_header - textColor = Prefs.iconColor.toLong() - backgroundDrawable = ColorDrawable(navHeader) - selectionSecondLineShown = false - cookies().forEach { (id, name) -> - profile(name = name ?: "") { - iconUrl = PROFILE_PICTURE_URL(id) - textColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = id - } - } - profileSetting(nameRes = R.string.kau_logout) { - iicon = GoogleMaterial.Icon.gmd_exit_to_app - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - identifier = -2L - } - profileSetting(nameRes = R.string.kau_add_account) { - iconDrawable = IconicsDrawable(this@MainActivity, GoogleMaterial.Icon.gmd_add).actionBar().paddingDp(5).color(Prefs.textColor) - textColor = Prefs.textColor.toLong() - identifier = -3L - } - profileSetting(nameRes = R.string.kau_manage_account) { - iicon = GoogleMaterial.Icon.gmd_settings - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - identifier = -4L - } - onProfileChanged { _, profile, current -> - if (current) launchWebOverlay(FbItem.PROFILE.url) - else when (profile.identifier) { - -2L -> { - val currentCookie = loadFbCookie(Prefs.userId) - if (currentCookie == null) { - toast(R.string.account_not_found) - FbCookie.reset { launchLogin(cookies(), true) } - } else { - materialDialogThemed { - title(R.string.kau_logout) - 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@MainActivity) } - } - } - } - -3L -> launchNewTask(LoginActivity::class.java, clearStack = false) - -4L -> launchNewTask(SelectorActivity::class.java, cookies(), false) - else -> { - switchUser(profile.identifier, { refreshAll() }) - tabsForEachView { _, view -> view.badgeText = null } - } - } - false - } - } - drawerHeader.setActiveProfile(Prefs.userId) - primaryFrostItem(FbItem.FEED_MOST_RECENT) - primaryFrostItem(FbItem.FEED_TOP_STORIES) - primaryFrostItem(FbItem.ACTIVITY_LOG) - divider() - primaryFrostItem(FbItem.PHOTOS) - primaryFrostItem(FbItem.GROUPS) - primaryFrostItem(FbItem.FRIENDS) - primaryFrostItem(FbItem.CHAT) - primaryFrostItem(FbItem.PAGES) - divider() - primaryFrostItem(FbItem.EVENTS) - primaryFrostItem(FbItem.BIRTHDAYS) - primaryFrostItem(FbItem.ON_THIS_DAY) - divider() - primaryFrostItem(FbItem.NOTES) - primaryFrostItem(FbItem.SAVED) - } - } - - private fun Builder.primaryFrostItem(item: FbItem) = this.primaryItem(item.titleId) { - iicon = item.icon - iconColor = Prefs.textColor.toLong() - textColor = Prefs.textColor.toLong() - selectedIconColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = item.titleId.toLong() - onClick { _ -> - frostAnswers { - logContentView(ContentViewEvent() - .putContentName(item.name) - .putContentType("drawer_item")) - } - launchWebOverlay(item.url) - false - } - } - - private fun Builder.secondaryFrostItem(@StringRes title: Int, onClick: () -> Unit) = this.secondaryItem(title) { - textColor = Prefs.textColor.toLong() - selectedIconColor = Prefs.textColor.toLong() - selectedTextColor = Prefs.textColor.toLong() - selectedColor = 0x00000001.toLong() - identifier = title.toLong() - onClick { _ -> onClick(); false } - } - - fun refreshAll() { - webFragmentObservable.onNext(WebFragment.REQUEST_REFRESH) - } - - 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) - searchViewBindIfNull { - bindSearchView(menu, R.id.action_search, Prefs.iconColor) { - textCallback = { query, _ -> - val results = searchViewCache[query] - if (results != null) - runOnUiThread { searchView?.results = results } - else - doAsync { - val data = SearchParser.query(query) ?: return@doAsync - val items = data.map { SearchItem(it.href, it.title, it.description) }.toMutableList() - if (items.isNotEmpty()) - items.add(SearchItem("${FbItem._SEARCH.url}?q=$query", string(R.string.show_all_results), iicon = null)) - searchViewCache.put(query, items) - uiThread { searchView?.results = items } - } - } - textDebounceInterval = 300 - searchCallback = { query, _ -> launchWebOverlay("${FbItem._SEARCH.url}/?q=$query"); true } - closeListener = { _ -> searchViewCache.clear() } - foregroundColor = Prefs.textColor - backgroundColor = Prefs.bgColor.withMinAlpha(200) - onItemClick = { _, key, _, _ -> launchWebOverlay(key) } - } - } - return true - } - - @SuppressLint("RestrictedApi") - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_settings -> { - val intent = Intent(this, SettingsActivity::class.java) - intent.putParcelableArrayListExtra(EXTRA_COOKIES, cookies()) - val bundle = ActivityOptionsCompat.makeCustomAnimation(this, R.anim.kau_slide_in_right, R.anim.kau_fade_out).toBundle() - startActivityForResult(intent, ACTIVITY_SETTINGS, bundle) - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - override fun openFileChooser(filePathCallback: ValueCallback<Array<Uri>?>, fileChooserParams: WebChromeClient.FileChooserParams) { - openMediaPicker(filePathCallback, fileChooserParams) - } - - @SuppressLint("NewApi") - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (onActivityResultWeb(requestCode, resultCode, data)) return - super.onActivityResult(requestCode, resultCode, data) - if (requestCode == ACTIVITY_SETTINGS) { - if (resultCode and REQUEST_RESTART_APPLICATION > 0) { //completely restart application - L.d("Restart Application Requested") - val intent = packageManager.getLaunchIntentForPackage(packageName) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) - val pending = PendingIntent.getActivity(this, 666, intent, PendingIntent.FLAG_CANCEL_CURRENT) - val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager - if (buildIsMarshmallowAndUp) - alarm.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) - else - alarm.setExact(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) - finish() - System.exit(0) - return - } - if (resultCode and REQUEST_RESTART > 0) return restart() - /* - * These results can be stacked - */ - if (resultCode and REQUEST_REFRESH > 0) webFragmentObservable.onNext(WebFragment.REQUEST_REFRESH) - if (resultCode and REQUEST_NAV > 0) frostNavigationBar() - if (resultCode and REQUEST_WEB_ZOOM > 0) webFragmentObservable.onNext(WebFragment.REQUEST_TEXT_ZOOM) - if (resultCode and REQUEST_SEARCH > 0) invalidateOptionsMenu() - } - } - - override fun onResume() { - super.onResume() - FbCookie.switchBackUser { } - } - - override fun onStart() { - //validate some pro features - if (!IS_FROST_PRO) { - if (Prefs.theme == Theme.CUSTOM.ordinal) Prefs.theme = Theme.DEFAULT.ordinal - } - super.onStart() - } - - override fun onDestroy() { - onDestroyBilling() - super.onDestroy() - } - - override fun backConsumer(): Boolean { - if (currentFragment.onBackPressed()) return true - if (Prefs.exitConfirmation) { - materialDialogThemed { - title(R.string.kau_exit) - content(R.string.kau_exit_confirmation) - positiveText(R.string.kau_yes) - negativeText(R.string.kau_no) - onPositive { _, _ -> finish() } - checkBoxPromptRes(R.string.kau_do_not_show_again, false, { _, b -> Prefs.exitConfirmation = !b }) - } - return true - } - return false - } - - inline val currentFragment - get() = supportFragmentManager.findFragmentByTag("android:switcher:${R.id.container}:${viewPager.currentItem}") as WebFragment - - inner class SectionsPagerAdapter(fm: FragmentManager, val pages: List<FbItem>) : FragmentPagerAdapter(fm) { - - override fun getItem(position: Int): Fragment { - val fragment = WebFragment(pages[position], position) - //If first load hasn't occurred, add a listener - if (!firstLoadFinished) { - var disposable: Disposable? = null - fragment.post { - disposable = it.web.refreshObservable.subscribe { - if (!it) { - //Ensure first load finisher only happens once - if (!firstLoadFinished) firstLoadFinished = true - disposable?.dispose() - disposable = null - } - } - } - } - return fragment - } - - override fun getCount() = pages.size - - override fun getPageTitle(position: Int): CharSequence = getString(pages[position].titleId) - } - - override val lowerVideoPadding: PointF - get() = - if (Prefs.mainActivityLayout == MainActivityLayout.BOTTOM_BAR) - PointF(0f, toolbar.height.toFloat()) - else - PointF(0f, 0f) - } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt index f17ccf20..19f5102a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/SettingsActivity.kt @@ -161,7 +161,7 @@ class SettingsActivity : KPrefActivity(), FrostBilling by IabSettings() { } fun shouldRestartMain() { - setFrostResult(MainActivity.REQUEST_RESTART) + setFrostResult(REQUEST_RESTART) } @SuppressLint("MissingSuperCall") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt index c41964cd..0dbbacbc 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -1,5 +1,6 @@ package com.pitchedapps.frost.activities +import android.annotation.SuppressLint import android.content.Intent import android.graphics.PointF import android.net.Uri @@ -18,15 +19,14 @@ import ca.allanwang.kau.utils.* import com.mikepenz.community_material_typeface_library.CommunityMaterial import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.pitchedapps.frost.R -import com.pitchedapps.frost.contracts.ActivityWebContract -import com.pitchedapps.frost.contracts.FileChooserContract -import com.pitchedapps.frost.contracts.FileChooserDelegate -import com.pitchedapps.frost.contracts.VideoViewHolder +import com.pitchedapps.frost.contracts.* import com.pitchedapps.frost.enums.OverlayContext import com.pitchedapps.frost.facebook.* import com.pitchedapps.frost.utils.* +import com.pitchedapps.frost.views.FrostContentWeb import com.pitchedapps.frost.views.FrostVideoViewer -import com.pitchedapps.frost.web.FrostWebView +import com.pitchedapps.frost.views.FrostWebView +import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import okhttp3.HttpUrl @@ -56,7 +56,7 @@ class FrostWebActivity : WebOverlayActivityBase(false) { * and pop a dialog giving the user the option to copy the shared text */ var disposable: Disposable? = null - disposable = frostWeb.web.refreshObservable.subscribe { + disposable = content.refreshObservable.subscribe { disposable?.dispose() materialDialogThemed { title(R.string.invalid_share_url) @@ -98,26 +98,36 @@ class WebOverlayBasicActivity : WebOverlayActivityBase(true) */ class WebOverlayActivity : WebOverlayActivityBase(false) +@SuppressLint("Registered") open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseActivity(), - ActivityWebContract, 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) - val frostWeb: FrostWebView by bindView(R.id.overlay_frost_webview) + val content: FrostContentWeb by bindView(R.id.frost_content_web) + val web: FrostWebView + get() = content.coreView val coordinator: CoordinatorLayout by bindView(R.id.overlay_main_content) - inline val urlTest: String? + private inline val urlTest: String? get() = intent.extras?.getString(ARG_URL) ?: intent.dataString - open val url: String + override val baseUrl: String get() = (intent.extras?.getString(ARG_URL) ?: intent.dataString).formattedFbUrl - inline val userId: Long + override val baseEnum: FbItem? = null + + private inline val userId: Long get() = intent.extras?.getLong(ARG_USER_ID, Prefs.userId) ?: Prefs.userId - inline val overlayContext: OverlayContext? + private inline val overlayContext: OverlayContext? get() = intent.extras?.getSerializable(ARG_OVERLAY_CONTEXT) as OverlayContext? + override fun setTitle(title: String) { + toolbar.title = title + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (urlTest == null) { @@ -136,17 +146,24 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc setFrostColors(toolbar, themeWindow = false) coordinator.setBackgroundColor(Prefs.bgColor.withAlpha(255)) - frostWeb.setupWebview(url) - if (forceBasicAgent) - frostWeb.web.userAgentString = USER_AGENT_BASIC - frostWeb.web.addTitleListener({ toolbar.title = it }) - Prefs.prevId = Prefs.userId - if (userId != Prefs.userId) FbCookie.switchUser(userId) { frostWeb.web.loadBaseUrl() } - else frostWeb.web.loadBaseUrl() - if (Showcase.firstWebOverlay) { - coordinator.frostSnackbar(R.string.web_overlay_swipe_hint) { - duration = Snackbar.LENGTH_INDEFINITE - setAction(R.string.kau_got_it) { _ -> this.dismiss() } + content.bind(this) + web.reloadBase(true) + + content.titleObservable + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { toolbar.title = it } + + with(web) { + if (forceBasicAgent) + userAgentString = USER_AGENT_BASIC + Prefs.prevId = Prefs.userId + if (userId != Prefs.userId) FbCookie.switchUser(userId) { reloadBase(true) } + else reloadBase(true) + if (Showcase.firstWebOverlay) { + coordinator.frostSnackbar(R.string.web_overlay_swipe_hint) { + duration = Snackbar.LENGTH_INDEFINITE + setAction(R.string.kau_got_it) { _ -> this.dismiss() } + } } } @@ -165,15 +182,15 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc super.onNewIntent(intent) val newUrl = (intent.extras?.getString(ARG_URL) ?: intent.dataString ?: return).formattedFbUrl L.d("New intent") - if (url != newUrl) { + if (baseUrl != newUrl) { this.intent = intent - frostWeb.web.baseUrl = newUrl - frostWeb.web.loadBaseUrl() + content.baseUrl = newUrl + web.reloadBase(true) } } override fun backConsumer(): Boolean { - if (!frostWeb.onBackPressed()) + if (!web.onBackPressed()) finishSlideOut() return true } @@ -216,9 +233,9 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseAc override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_copy_link -> copyToClipboard(frostWeb.web.url) - R.id.action_share -> shareText(frostWeb.web.url) - else -> if (!OverlayContext.onOptionsItemSelected(frostWeb.web, item.itemId)) + R.id.action_copy_link -> copyToClipboard(web.currentUrl) + R.id.action_share -> shareText(web.currentUrl) + else -> if (!OverlayContext.onOptionsItemSelected(web, item.itemId)) return super.onOptionsItemSelected(item) } return true |