From 16b4932a013dd6d218243a35225b85ceb464c7c9 Mon Sep 17 00:00:00 2001 From: Administrator <1760316362@qq.com> Date: Tue, 30 Jul 2019 10:37:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9setEdgeEffectColor=EF=BC=8C?= =?UTF-8?q?=E9=80=82=E9=85=8DViewPager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/base/BaseActivity.kt | 4 +- .../main/java/io/legado/app/lib/theme/ATH.kt | 38 ++++++++++++- .../io/legado/app/ui/about/AboutActivity.kt | 2 +- .../io/legado/app/ui/about/DonateActivity.kt | 2 +- .../app/ui/bookinfo/BookInfoActivity.kt | 2 +- .../app/ui/bookinfo/BookInfoEditActivity.kt | 2 +- .../app/ui/bookshelf/BookshelfActivity.kt | 2 +- .../app/ui/booksource/BookSourceActivity.kt | 2 +- .../io/legado/app/ui/config/ConfigActivity.kt | 2 +- .../io/legado/app/ui/main/MainActivity.kt | 6 +- .../ui/main/bookshelf/BookshelfFragment.kt | 3 + .../app/ui/main/findbook/FindBookFragment.kt | 2 +- .../app/ui/main/my/PreferenceFragment.kt | 8 +++ .../io/legado/app/ui/main/rss/RssFragment.kt | 5 +- .../io/legado/app/ui/qrcode/QrCodeActivity.kt | 2 +- .../app/ui/replacerule/ReplaceRuleActivity.kt | 2 +- .../io/legado/app/ui/search/SearchActivity.kt | 2 +- .../app/ui/sourcedebug/SourceDebugActivity.kt | 2 +- .../app/ui/sourceedit/SourceEditActivity.kt | 2 +- .../legado/app/ui/welcome/WelcomeActivity.kt | 2 +- .../main/res/layout/fragment_bookshelf.xml | 55 ++++++++++--------- 21 files changed, 98 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 103ce40cf..4dfe09fb4 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -30,7 +30,7 @@ abstract class BaseActivity(private val fullScreen: Boolean = tr setupSystemBar() super.onCreate(savedInstanceState) setContentView(layoutID) - onViewModelCreated(viewModel, savedInstanceState) + onActivityCreated(viewModel, savedInstanceState) observeLiveBus() } @@ -39,7 +39,7 @@ abstract class BaseActivity(private val fullScreen: Boolean = tr cancel() } - abstract fun onViewModelCreated(viewModel: VM, savedInstanceState: Bundle?) + abstract fun onActivityCreated(viewModel: VM, savedInstanceState: Bundle?) final override fun onCreateOptionsMenu(menu: Menu?): Boolean { return menu?.let { diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index 98fb11c35..7b5ffd1d1 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -11,6 +11,7 @@ import android.widget.EdgeEffect import androidx.annotation.ColorInt import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager.widget.ViewPager import io.legado.app.utils.getPrefBoolean @@ -28,7 +29,10 @@ object ATH { } fun setStatusbarColorAuto(activity: Activity) { - setStatusbarColor(activity, ThemeStore.statusBarColor(activity, activity.getPrefBoolean("transparentStatusBar"))) + setStatusbarColor( + activity, + ThemeStore.statusBarColor(activity, activity.getPrefBoolean("transparentStatusBar")) + ) } fun setStatusbarColor(activity: Activity, color: Int) { @@ -116,8 +120,8 @@ object ATH { return dialog } - fun setEdgeEffectColor(view: RecyclerView, color: Int) { - view.edgeEffectFactory = object : RecyclerView.EdgeEffectFactory() { + fun setEdgeEffectColor(view: RecyclerView?, @ColorInt color: Int) { + view?.edgeEffectFactory = object : RecyclerView.EdgeEffectFactory() { override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect { val edgeEffect = super.createEdgeEffect(view, direction) edgeEffect.color = color @@ -125,4 +129,32 @@ object ATH { } } } + + fun setEdgeEffectColor(viewPager: ViewPager?, @ColorInt color: Int) { + try { + val clazz = ViewPager::class.java + for (name in arrayOf("mLeftEdge", "mRightEdge")) { + val field = clazz.getDeclaredField(name) + field.isAccessible = true + val edge = field.get(viewPager) + (edge as EdgeEffect).color = color + } + } catch (ignored: Exception) { + } + } + + fun applyEdgeEffectColor(view: View?) { + when (view) { + is RecyclerView -> view.edgeEffectFactory = DEFAULT_EFFECT_FACTORY + is ViewPager -> setEdgeEffectColor(view, ThemeStore.primaryColor(view.context)) + } + } + + private val DEFAULT_EFFECT_FACTORY = object : RecyclerView.EdgeEffectFactory() { + override fun createEdgeEffect(view: RecyclerView, direction: Int): EdgeEffect { + val edgeEffect = super.createEdgeEffect(view, direction) + edgeEffect.color = ThemeStore.primaryColor(view.context) + return edgeEffect + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt index c50e14a46..05eef3ad7 100644 --- a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt +++ b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt @@ -17,7 +17,7 @@ class AboutActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_about - override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { val fTag = "aboutFragment" var aboutFragment = supportFragmentManager.findFragmentByTag(fTag) if (aboutFragment == null) aboutFragment = AboutFragment() diff --git a/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt b/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt index 432e99a58..b27bfb654 100644 --- a/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt +++ b/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt @@ -30,7 +30,7 @@ class DonateActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_donate - override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { setSupportActionBar(toolbar) vw_zfb_tz.setOnClickListener { aliDonate(this) } cv_wx_gzh.setOnClickListener { diff --git a/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoActivity.kt index 083fb8823..39adfaa01 100644 --- a/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoActivity.kt @@ -11,7 +11,7 @@ class BookInfoActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_book_info - override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { } diff --git a/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoEditActivity.kt b/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoEditActivity.kt index 4572b425d..8e3ed92c5 100644 --- a/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/bookinfo/BookInfoEditActivity.kt @@ -11,7 +11,7 @@ class BookInfoEditActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_book_info_edit - override fun onViewModelCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: BookInfoViewModel, savedInstanceState: Bundle?) { } diff --git a/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt index 6ba11119f..15a920dca 100644 --- a/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt +++ b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt @@ -24,7 +24,7 @@ class BookshelfActivity : BaseActivity() { private lateinit var bookshelfAdapter: BookshelfAdapter private var bookshelfLiveData: LiveData>? = null - override fun onViewModelCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) { if (viewModel.bookGroup == null) { viewModel.bookGroup = intent.getParcelableExtra("data") } diff --git a/app/src/main/java/io/legado/app/ui/booksource/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/booksource/BookSourceActivity.kt index ceb1d28da..bdc43ffeb 100644 --- a/app/src/main/java/io/legado/app/ui/booksource/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/booksource/BookSourceActivity.kt @@ -37,7 +37,7 @@ class BookSourceActivity : BaseActivity(), BookSourceAdapter private lateinit var adapter: BookSourceAdapter private var bookSourceLiveDate: LiveData>? = null - override fun onViewModelCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) { setSupportActionBar(toolbar) initRecyclerView() initDataObserve() diff --git a/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt b/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt index 26894c7e2..a8449be35 100644 --- a/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt +++ b/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt @@ -13,7 +13,7 @@ class ConfigActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_config - override fun onViewModelCreated(viewModel: ConfigViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: ConfigViewModel, savedInstanceState: Bundle?) { intent.getIntExtra("configType", -1).let { if (it != -1) viewModel.configType = it } diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 5d14017c0..efe3c82a1 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -14,6 +14,7 @@ import io.legado.app.base.BaseActivity import io.legado.app.constant.Bus import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat +import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.Selector import io.legado.app.lib.theme.ThemeStore import io.legado.app.ui.main.bookshelf.BookshelfFragment @@ -35,10 +36,11 @@ class MainActivity : BaseActivity(), BottomNavigationView.OnNavig override val layoutID: Int get() = R.layout.activity_main - override fun onViewModelCreated(viewModel: MainViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: MainViewModel, savedInstanceState: Bundle?) { + ATH.applyEdgeEffectColor(view_pager_main) bottom_navigation_view.setBackgroundColor(ThemeStore.backgroundColor(this)) val colorStateList = Selector.colorBuild() - .setDefaultColor(bottom_navigation_view.context.getCompatColor(R.color.btn_bg_press_tp)) + .setDefaultColor(getCompatColor(R.color.btn_bg_press_tp)) .setSelectedColor(ThemeStore.primaryColor(bottom_navigation_view.context)).create() bottom_navigation_view.itemIconTintList = colorStateList bottom_navigation_view.itemTextColor = colorStateList diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index afb29d680..2947321bc 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -17,11 +17,13 @@ import io.legado.app.R import io.legado.app.base.BaseFragment import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookGroup +import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ThemeStore import io.legado.app.ui.bookshelf.BookshelfActivity import io.legado.app.ui.search.SearchActivity import io.legado.app.utils.disableAutoFill import kotlinx.android.synthetic.main.fragment_bookshelf.* +import kotlinx.android.synthetic.main.fragment_find_book.* import kotlinx.android.synthetic.main.view_title_bar.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch @@ -58,6 +60,7 @@ class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf), SearchView. } private fun initRecyclerView() { + ATH.applyEdgeEffectColor(rv_bookshelf) refresh_layout.setColorSchemeColors(ThemeStore.accentColor(refresh_layout.context)) refresh_layout.setOnRefreshListener { refresh_layout.isRefreshing = false diff --git a/app/src/main/java/io/legado/app/ui/main/findbook/FindBookFragment.kt b/app/src/main/java/io/legado/app/ui/main/findbook/FindBookFragment.kt index 3f59758d2..c8ef1245d 100644 --- a/app/src/main/java/io/legado/app/ui/main/findbook/FindBookFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/findbook/FindBookFragment.kt @@ -33,7 +33,7 @@ class FindBookFragment : BaseFragment(R.layout.fragment_find_book) { } private fun initRecyclerView() { - ATH.setEdgeEffectColor(rv_find, ThemeStore.primaryColor(rv_find.context)) + ATH.applyEdgeEffectColor(rv_find) rv_find.layoutManager = FlexboxLayoutManager(context) adapter = FindBookAdapter() rv_find.adapter = adapter diff --git a/app/src/main/java/io/legado/app/ui/main/my/PreferenceFragment.kt b/app/src/main/java/io/legado/app/ui/main/my/PreferenceFragment.kt index 50071d5e7..4f24b9e75 100644 --- a/app/src/main/java/io/legado/app/ui/main/my/PreferenceFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/my/PreferenceFragment.kt @@ -2,10 +2,13 @@ package io.legado.app.ui.main.my import android.content.SharedPreferences import android.os.Bundle +import android.view.View import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import io.legado.app.App import io.legado.app.R +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore import io.legado.app.ui.about.AboutActivity import io.legado.app.ui.about.DonateActivity import io.legado.app.ui.booksource.BookSourceActivity @@ -20,6 +23,11 @@ class PreferenceFragment : PreferenceFragmentCompat(), addPreferencesFromResource(R.xml.pref_main) } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + ATH.applyEdgeEffectColor(listView) + } + override fun onResume() { super.onResume() preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this) diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt index 4b12972ec..d13c1d8e1 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt @@ -4,6 +4,9 @@ import android.os.Bundle import android.view.View import io.legado.app.R import io.legado.app.base.BaseFragment +import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.ThemeStore +import kotlinx.android.synthetic.main.fragment_rss.* class RssFragment : BaseFragment(R.layout.fragment_rss) { @@ -12,6 +15,6 @@ class RssFragment : BaseFragment(R.layout.fragment_rss) { } private fun initRecyclerView() { - + ATH.applyEdgeEffectColor(recycler_view) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt index 9d84fffea..deb50d3a9 100644 --- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt @@ -26,7 +26,7 @@ class QrCodeActivity : BaseActivity(), QRCodeView.Delegate { private val requestQrImage = 202 private var flashlightIsOpen: Boolean = false - override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { setSupportActionBar(toolbar) zxingview.setDelegate(this) fab_flashlight.setOnClickListener { diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 02cb97c4a..eac5212ee 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -31,7 +31,7 @@ class ReplaceRuleActivity : BaseActivity() { private var rulesLiveData: LiveData>? = null private var allEnabled = false - override fun onViewModelCreated(viewModel: ReplaceRuleViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: ReplaceRuleViewModel, savedInstanceState: Bundle?) { initRecyclerView() initDataObservers() initSwipeToDelete() diff --git a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt index e09085223..644684e44 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchActivity.kt @@ -15,7 +15,7 @@ class SearchActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_search - override fun onViewModelCreated(viewModel: SearchViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: SearchViewModel, savedInstanceState: Bundle?) { initSearchView() viewModel.search() } diff --git a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt index 0d0a9f014..af7dfe808 100644 --- a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugActivity.kt @@ -30,7 +30,7 @@ class SourceDebugActivity : BaseActivity(), SourceDebug.Callba private lateinit var adapter: SourceDebugAdapter private var bookSource: BookSource? = null - override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { launch(IO) { intent.getStringExtra("key")?.let { bookSource = App.db.bookSourceDao().findByKey(it) diff --git a/app/src/main/java/io/legado/app/ui/sourceedit/SourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/sourceedit/SourceEditActivity.kt index 03e1287a0..787ee6868 100644 --- a/app/src/main/java/io/legado/app/ui/sourceedit/SourceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/sourceedit/SourceEditActivity.kt @@ -42,7 +42,7 @@ class SourceEditActivity : BaseActivity(false), KeyboardToo private var mSoftKeyboardTool: PopupWindow? = null private var mIsSoftKeyBoardShowing = false - override fun onViewModelCreated(viewModel: SourceEditViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: SourceEditViewModel, savedInstanceState: Bundle?) { initView() viewModel.sourceLiveData.observe(this, Observer { upRecyclerView(it) diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 587794914..f225bcd80 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -18,7 +18,7 @@ class WelcomeActivity : BaseActivity() { override val layoutID: Int get() = R.layout.activity_welcome - override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { + override fun onActivityCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) { iv_bg.setColorFilter(ThemeStore.accentColor(this)) val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) welAnimator.startDelay = 100 diff --git a/app/src/main/res/layout/fragment_bookshelf.xml b/app/src/main/res/layout/fragment_bookshelf.xml index d5f6c6819..5e34af041 100644 --- a/app/src/main/res/layout/fragment_bookshelf.xml +++ b/app/src/main/res/layout/fragment_bookshelf.xml @@ -1,42 +1,43 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:id="@+id/title_bar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:attachToActivity="false" + app:title="@string/bookshelf"/> + android:id="@+id/rv_book_group" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:overScrollMode="never"/> + android:id="@+id/tv_recent_reading" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/background" + android:elevation="3dp" + android:focusable="true" + android:padding="5dp" + android:text="@string/recent_reading"/> + android:id="@+id/refresh_layout" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + android:id="@+id/rv_bookshelf" + android:layout_width="match_parent" + android:layout_height="match_parent"/> \ No newline at end of file