diff --git a/.github/workflows/run-gradle-pr.yml b/.github/workflows/run-gradle-pr.yml new file mode 100644 index 0000000..1e92655 --- /dev/null +++ b/.github/workflows/run-gradle-pr.yml @@ -0,0 +1,29 @@ +name: Run Gradle on PRs +on: + pull_request: + types: [ opened, synchronize, reopened ] +jobs: + gradle: + strategy: + matrix: + os: [macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Create Firebase Google Services JSON from Github Secret + env: + GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} + run: touch ./app/google-services.json && echo $GOOGLE_SERVICES_JSON > ./app/google-services.json + - uses: actions/setup-java@v3 + with: + distribution: zulu + java-version: 17 + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Execute Gradle build + run: ./gradlew build \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3378229..906a17d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/build.gradle b/app/build.gradle index 9ff6764..75643ff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,26 +1,31 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-parcelize' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { - compileSdkVersion 29 + namespace 'com.flamyoad.tsukiviewer' + compileSdkVersion 34 compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = "17" + } + + buildFeatures { + viewBinding true } defaultConfig { applicationId "com.flamyoad.tsukiviewer" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 34 versionCode 12 versionName "3.2.0" @@ -70,7 +75,7 @@ dependencies { implementation "androidx.preference:preference-ktx:$preference_version" - implementation 'gun0912.ted:tedpermission:2.2.3' + implementation 'io.github.ParkSangGwon:tedpermission-coroutine:3.3.0' def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @@ -79,19 +84,19 @@ dependencies { implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" - def lifecycle_version = "2.2.0" + def lifecycle_version = "2.6.2" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" - def room_version = "2.2.5" + def room_version = "2.6.1" implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" implementation "androidx.room:room-ktx:$room_version" testImplementation "androidx.room:room-testing:$room_version" androidTestImplementation "androidx.room:room-testing:$room_version" - def paging_version = "2.1.2" + def paging_version = "3.2.1" implementation "androidx.paging:paging-runtime-ktx:$paging_version" implementation 'com.github.bumptech.glide:glide:4.11.0' @@ -99,7 +104,7 @@ dependencies { implementation 'com.github.chrisbanes:PhotoView:2.3.0' - implementation 'com.google.android:flexbox:2.0.1' + implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation 'com.quiph.ui:recyclerviewfastscroller:0.2.1' @@ -120,10 +125,10 @@ dependencies { implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' // Kotlin Flow - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" implementation 'jp.wasabeef:recyclerview-animators:4.0.2' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c6e9c88..7f6a393 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,11 +1,15 @@ - - + + + + + - + diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/MainActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/MainActivity.kt index 5a3d73c..6a23ae8 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/MainActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/MainActivity.kt @@ -16,39 +16,42 @@ import androidx.core.content.ContextCompat import androidx.core.view.GravityCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import com.flamyoad.tsukiviewer.databinding.ActivityMainBinding import com.flamyoad.tsukiviewer.ui.home.bookmarks.BookmarkFragment import com.flamyoad.tsukiviewer.ui.home.collections.CollectionFragment import com.flamyoad.tsukiviewer.ui.home.local.LocalDoujinsFragment import com.flamyoad.tsukiviewer.ui.home.tags.DoujinTagsFragment import com.flamyoad.tsukiviewer.ui.settings.SettingsActivity import com.google.android.material.navigation.NavigationView -import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { + private lateinit var binding: ActivityMainBinding + private var callback: ActionMode.Callback? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) val toggle = ActionBarDrawerToggle( this, - drawerLayout, - toolbar, + binding.drawerLayout, + binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) - drawerLayout.addDrawerListener(toggle) - drawerLayout.drawerElevation = 0f - drawerLayout.setScrimColor(ContextCompat.getColor(this, R.color.navDrawerScrim)) + binding.drawerLayout.addDrawerListener(toggle) + binding.drawerLayout.drawerElevation = 0f + binding.drawerLayout.setScrimColor(ContextCompat.getColor(this, R.color.navDrawerScrim)) toggle.syncState() - nav_view.setNavigationItemSelectedListener(this) + binding.navView.setNavigationItemSelectedListener(this) if (savedInstanceState == null) { addFragment(LocalDoujinsFragment.newInstance(), LocalDoujinsFragment.APPBAR_TITLE) @@ -70,8 +73,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte // Note to self: onOptionsItemSelected() event actually bubbles up from Activity to Fragment. // So we have to return false here to allow menu processing to proceed. - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { R.id.action_search_local -> { /* There is no need to animate the closing of DrawerLayout if we start a new activity. This is because the animation has to be completed before the new activity can be started. @@ -79,12 +82,12 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte Activity starting code is in LocalDoujinFragment.kt */ - drawerLayout.closeDrawer(GravityCompat.START, false) + binding.drawerLayout.closeDrawer(GravityCompat.START, false) return false } else -> { - drawerLayout.closeDrawer(GravityCompat.START) + binding.drawerLayout.closeDrawer(GravityCompat.START) return false } } @@ -149,7 +152,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte startActivity(intent) } } - drawerLayout.closeDrawer(GravityCompat.START) + binding.drawerLayout.closeDrawer(GravityCompat.START) return true } @@ -177,23 +180,23 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } private fun removeAppBarShadow() { - appbarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(this, R.animator.appbar_elevation_off) + binding.appbarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(this, R.animator.appbar_elevation_off) } private fun showAppBarShadow() { - appbarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(this, R.animator.appbar_elevation_on) + binding.appbarLayout.stateListAnimator = AnimatorInflater.loadStateListAnimator(this, R.animator.appbar_elevation_on) } override fun onBackPressed() { - if (drawerLayout.isDrawerOpen(GravityCompat.START)) { - drawerLayout.closeDrawer(GravityCompat.START) + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START) return } if (supportFragmentManager.backStackEntryCount > 0) { clearFragmentBackStack() setTitle(LocalDoujinsFragment.APPBAR_TITLE) - nav_view.setCheckedItem(R.id.nav_localdoujins) + binding.navView.setCheckedItem(R.id.nav_localdoujins) } else { if (MyAppPreference.getInstance(this).askBeforeQuit()) { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/model/BookmarkItem.kt b/app/src/main/java/com/flamyoad/tsukiviewer/model/BookmarkItem.kt index cf018a6..062cae9 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/model/BookmarkItem.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/model/BookmarkItem.kt @@ -3,7 +3,6 @@ package com.flamyoad.tsukiviewer.model import android.os.Parcelable import androidx.room.* import com.flamyoad.tsukiviewer.db.typeconverter.FolderConverter -import kotlinx.android.parcel.Parcelize import java.io.File @Entity( diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/model/Doujin.kt b/app/src/main/java/com/flamyoad/tsukiviewer/model/Doujin.kt index 6d189a3..7550834 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/model/Doujin.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/model/Doujin.kt @@ -4,7 +4,7 @@ import android.net.Uri import android.os.Parcelable import androidx.core.net.toUri import com.flamyoad.tsukiviewer.utils.extensions.imageExtensions -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.io.File data class Doujin( diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/network/FetchMetadataService.kt b/app/src/main/java/com/flamyoad/tsukiviewer/network/FetchMetadataService.kt index c3d5898..69b7e08 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/network/FetchMetadataService.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/network/FetchMetadataService.kt @@ -209,6 +209,8 @@ class FetchMetadataService : Service() { FetchStatus.SUCCESS -> postFetchHistory(result) FetchStatus.ALREADY_EXISTS -> postFetchHistory(result) FetchStatus.NO_MATCH -> postFetchHistory(result) + FetchStatus.NETWORK_ERROR -> { /* Handle error if needed */ } + FetchStatus.NONE -> { /* Do nothing */ } } if (fetchStatus != FetchStatus.ALREADY_EXISTS) { @@ -229,6 +231,10 @@ class FetchMetadataService : Service() { when (result.status) { FetchStatus.NO_MATCH -> showToast("No matching result for this directory") + FetchStatus.SUCCESS, + FetchStatus.ALREADY_EXISTS, + FetchStatus.NETWORK_ERROR, + FetchStatus.NONE -> { /* Do nothing */ } } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/repository/BookmarkRepository.kt b/app/src/main/java/com/flamyoad/tsukiviewer/repository/BookmarkRepository.kt index 5b4214b..4f5ace8 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/repository/BookmarkRepository.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/repository/BookmarkRepository.kt @@ -165,7 +165,7 @@ class BookmarkRepository(private val context: Context) { return@withTransaction stringBuilder.toString() } catch (e: Exception) { - Log.e("db", e.message) + Log.e("db", e.message ?: "Unknown error") e.printStackTrace() return@withTransaction "Failed to add or remove current doujin" } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DialogViewStyle.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DialogViewStyle.kt index 4ea8344..e8ae56b 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DialogViewStyle.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DialogViewStyle.kt @@ -11,7 +11,6 @@ import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.flamyoad.tsukiviewer.R -import kotlinx.android.synthetic.main.dialog_grid_view_style.* class DialogViewStyle : DialogFragment() { private val viewModel: DoujinViewModel by activityViewModels() diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DoujinDetailsActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DoujinDetailsActivity.kt index 91a6ae0..53499e2 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DoujinDetailsActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/DoujinDetailsActivity.kt @@ -12,19 +12,22 @@ import androidx.viewpager.widget.ViewPager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.DoujinPagerAdapter import com.flamyoad.tsukiviewer.adapter.LocalDoujinsAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityDoujinDetailsBinding import com.flamyoad.tsukiviewer.ui.editor.EditorActivity import com.flamyoad.tsukiviewer.utils.ActivityStackUtils import com.flamyoad.tsukiviewer.utils.extensions.toast import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.activity_doujin_details.* class DoujinDetailsActivity : AppCompatActivity() { + private lateinit var binding: ActivityDoujinDetailsBinding + private val viewModel: DoujinViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_doujin_details) + binding = ActivityDoujinDetailsBinding.inflate(layoutInflater) + setContentView(binding.root) val dirPath = intent.getStringExtra(LocalDoujinsAdapter.DOUJIN_FILE_PATH) ?: "" viewModel.scanForImages(dirPath) @@ -37,7 +40,7 @@ class DoujinDetailsActivity : AppCompatActivity() { return@Observer } - Snackbar.make(rootView, text, Snackbar.LENGTH_LONG) + Snackbar.make(binding.rootView, text, Snackbar.LENGTH_LONG) .show() viewModel.snackbarText.value = "" @@ -56,8 +59,8 @@ class DoujinDetailsActivity : AppCompatActivity() { handleBackPress() } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { android.R.id.home -> { handleBackPress() } @@ -86,21 +89,21 @@ class DoujinDetailsActivity : AppCompatActivity() { private fun initViewPager(savedInstanceState: Bundle?) { val adapterViewPager = DoujinPagerAdapter(supportFragmentManager) - viewpager.adapter = adapterViewPager - tabLayout.setupWithViewPager(viewpager) + binding.viewpager.adapter = adapterViewPager + binding.tabLayout.setupWithViewPager(binding.viewpager) viewModel.landingPage().observe(this, Observer { // Do not trigger on screen rotation. Only trigger on first time when entering activity if (savedInstanceState != null) return@Observer when (it) { - LandingPageMode.DoujinDetails -> viewpager.setCurrentItem(0, false) - LandingPageMode.ImageGrid -> viewpager.setCurrentItem(1, false) - else -> viewpager.setCurrentItem(0, false) + LandingPageMode.DoujinDetails -> binding.viewpager.setCurrentItem(0, false) + LandingPageMode.ImageGrid -> binding.viewpager.setCurrentItem(1, false) + else -> binding.viewpager.setCurrentItem(0, false) } }) - viewpager.addOnPageChangeListener(object: ViewPager.OnPageChangeListener { + binding.viewpager.addOnPageChangeListener(object: ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { invalidateOptionsMenu() } @@ -110,7 +113,7 @@ class DoujinDetailsActivity : AppCompatActivity() { } private fun initToolbar() { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentDoujinDetails.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentDoujinDetails.kt index ee0ed8b..b8b3aee 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentDoujinDetails.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentDoujinDetails.kt @@ -26,21 +26,29 @@ import com.flamyoad.tsukiviewer.utils.TimeUtils import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.synthetic.main.doujin_details_tags_group.* -import kotlinx.android.synthetic.main.fragment_doujin_details.* +import com.flamyoad.tsukiviewer.databinding.FragmentDoujinDetailsBinding import java.io.File import java.util.* private const val COLLECTION_DIALOG_TAG = "collection_dialog" class FragmentDoujinDetails : Fragment(), SelectSourceListener { + private var _binding: FragmentDoujinDetailsBinding? = null + private val binding get() = _binding!! + private val viewModel: DoujinViewModel by activityViewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_doujin_details, container, false) + _binding = FragmentDoujinDetailsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -70,18 +78,18 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { Glide.with(this) .load(image) .sizeMultiplier(0.75f) - .into(imgBackground) + .into(binding.imgBackground) Glide.with(this) .load(image) - .into(imgCover) + .into(binding.imgCover) }) } override fun onStop() { super.onStop() - Glide.with(this).clear(imgBackground) - Glide.with(this).clear(imgCover) + Glide.with(this).clear(binding.imgBackground) + Glide.with(this).clear(binding.imgCover) } private fun initUi() { @@ -89,11 +97,11 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { Glide.with(this) .load(image) .sizeMultiplier(0.75f) - .into(imgBackground) + .into(binding.imgBackground) Glide.with(this) .load(image) - .into(imgCover) + .into(binding.imgCover) }) viewModel.detailWithTags.observe(viewLifecycleOwner, Observer { @@ -102,29 +110,29 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { val dir = File(currentPath ?: "") - txtDirectory.text = dir.absolutePath - txtDateModified.text = TimeUtils.getReadableDate(dir.lastModified()) + binding.txtDirectory.text = dir.absolutePath + binding.txtDateModified.text = TimeUtils.getReadableDate(dir.lastModified()) if (it == null) { // Show directory name if metadata not yet obtained from API - txtTitleEng.text = dir.name + binding.txtTitleEng.text = dir.name // Hides the tag group in case the user deletes the title & tags - tagGroup.visibility = View.GONE - tagsNotFoundIndicator.visibility = View.VISIBLE + binding.tagGroup.root.visibility = View.GONE + binding.tagsNotFoundIndicator.root.visibility = View.VISIBLE } else { // Shows the tag group if data is found in database - tagGroup.visibility = View.VISIBLE - tagsNotFoundIndicator.visibility = View.INVISIBLE + binding.tagGroup.root.visibility = View.VISIBLE + binding.tagsNotFoundIndicator.root.visibility = View.INVISIBLE initDoujinDetails(it) } }) viewModel.imageList().observe(viewLifecycleOwner, Observer { - txtImageCount.text = it.size.toString() + binding.txtImageCount.text = it.size.toString() }) - fab.setOnClickListener { + binding.fab.setOnClickListener { viewModel.fetchBookmarkGroup() openCollectionDialog() } @@ -133,7 +141,7 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { private fun initDoujinDetails(item: DoujinDetailsWithTags) { initColoredEnglishTitle(item.doujinDetails) - txtTitleJap.text = item.doujinDetails.fullTitleJapanese + binding.txtTitleJap.text = item.doujinDetails.fullTitleJapanese val parodies = item.tags.filter { x -> x.type == "parody" } val chars = item.tags.filter { x -> x.type == "character" } @@ -163,13 +171,13 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { } val recyclerView = when (i) { - 0 -> listParodies - 1 -> listCharacters - 2 -> listTags - 3 -> listArtists - 4 -> listGroups - 5 -> listLanguages - 6 -> listCategories + 0 -> binding.tagGroup.listParodies + 1 -> binding.tagGroup.listCharacters + 2 -> binding.tagGroup.listTags + 3 -> binding.tagGroup.listArtists + 4 -> binding.tagGroup.listGroups + 5 -> binding.tagGroup.listLanguages + 6 -> binding.tagGroup.listCategories else -> null } @@ -187,7 +195,7 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { // No need to prettify the title if short title does not exist if (shortTitleEnglish.isBlank()) { - txtTitleEng.text = doujinDetails.fullTitleEnglish + binding.txtTitleEng.text = doujinDetails.fullTitleEnglish return } @@ -196,7 +204,7 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { // If the short title is not found inside full title. Then we don't have to prettify it. if (indexOfShortTitle == -1) { - txtTitleEng.text = doujinDetails.fullTitleEnglish + binding.txtTitleEng.text = doujinDetails.fullTitleEnglish return } @@ -210,7 +218,7 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { ) } - txtTitleEng.text = coloredTitle + binding.txtTitleEng.text = coloredTitle } private fun openCollectionDialog() { @@ -239,7 +247,7 @@ class FragmentDoujinDetails : Fragment(), SelectSourceListener { override fun onFetchMetadata(sources: EnumSet) { if (viewModel.detailsNotExists()) { val dirPath = - requireActivity().intent.getStringExtra(LocalDoujinsAdapter.DOUJIN_FILE_PATH) + requireActivity().intent.getStringExtra(LocalDoujinsAdapter.DOUJIN_FILE_PATH) ?: return FetchMetadataService.startService(requireContext(), dirPath, sources) } else { viewModel.resetTags(sources) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentGridImages.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentGridImages.kt index 95ebbb4..1c45a6a 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentGridImages.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/doujinpage/FragmentGridImages.kt @@ -15,8 +15,8 @@ import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.DoujinImagesAdapter import com.flamyoad.tsukiviewer.adapter.DoujinImagesAdapter.ItemType import com.flamyoad.tsukiviewer.adapter.LocalDoujinsAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentGridImagesBinding import com.flamyoad.tsukiviewer.utils.ui.GridItemDecoration -import kotlinx.android.synthetic.main.fragment_grid_images.* const val GRID_ITEM_SPAN_PORTRAIT = 3 const val GRID_ITEM_SPAN_LANDSCAPE = 5 @@ -31,6 +31,9 @@ const val IMAGE_POSITION_REQUEST_CODE = 100 const val DIALOG_VIEW_STYLE = "dialog_view_style" class FragmentGridImages : Fragment() { + private var _binding: FragmentGridImagesBinding? = null + private val binding get() = _binding!! + private val viewModel by activityViewModels() private var adapter: DoujinImagesAdapter? = null @@ -43,7 +46,13 @@ class FragmentGridImages : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_grid_images, container, false) + _binding = FragmentGridImagesBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onResume() { @@ -152,15 +161,15 @@ class FragmentGridImages : Fragment() { gridLayoutManager = GridLayoutManager(context, spanCount) - listImages.adapter = adapter - listImages.layoutManager = gridLayoutManager + binding.listImages.adapter = adapter + binding.listImages.layoutManager = gridLayoutManager /* Since this method is called each time the view type is changed, We have to clear the item decorations added previously. Otherwise, the decors stack on top of other. 1dp will become 2dp, 2 dp will become 3dp and so on... */ - while (listImages.itemDecorationCount > 0) { - listImages.removeItemDecorationAt(0) + while (binding.listImages.itemDecorationCount > 0) { + binding.listImages.removeItemDecorationAt(0) } val itemDecoration = @@ -170,7 +179,7 @@ class FragmentGridImages : Fragment() { includeEdge = false ) - listImages.addItemDecoration(itemDecoration) + binding.listImages.addItemDecoration(itemDecoration) viewModel.imageList().observe(viewLifecycleOwner, Observer { adapter?.setList(it) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorActivity.kt index 4f42931..2e52adf 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorActivity.kt @@ -8,21 +8,25 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.children import androidx.lifecycle.Observer import com.flamyoad.tsukiviewer.R +import com.flamyoad.tsukiviewer.databinding.ActivityEditorBinding import com.flamyoad.tsukiviewer.model.Tag import com.flamyoad.tsukiviewer.utils.extensions.toast import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup -import kotlinx.android.synthetic.main.activity_editor.* private const val BACKMOST_POSITION = -1 class EditorActivity : AppCompatActivity(), CreateTagListener { + + private lateinit var binding: ActivityEditorBinding + private val viewModel: EditorViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_editor) + binding = ActivityEditorBinding.inflate(layoutInflater) + setContentView(binding.root) initToolbar() initTagGroups() @@ -32,8 +36,8 @@ class EditorActivity : AppCompatActivity(), CreateTagListener { toast("Data is saved") finish() } else { - contentLayout.isEnabled = false - parentLayout.alpha = 0.5f + binding.contentLayout.isEnabled = false + binding.parentLayout.alpha = 0.5f } }) } @@ -43,8 +47,8 @@ class EditorActivity : AppCompatActivity(), CreateTagListener { return true } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { android.R.id.home -> { finish() } @@ -72,14 +76,14 @@ class EditorActivity : AppCompatActivity(), CreateTagListener { } private fun initToolbar() { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.apply { setDisplayHomeAsUpEnabled(true) setDisplayShowHomeEnabled(true) } val title = intent.getStringExtra(DOUJIN_NAME) - txtDoujinTitle.text = title + binding.txtDoujinTitle.text = title } private fun initTagGroups() { @@ -96,31 +100,31 @@ class EditorActivity : AppCompatActivity(), CreateTagListener { } viewModel.parody.observe(this, Observer { - listParodies.setTagList("parody", it) + binding.listParodies.setTagList("parody", it) }) viewModel.character.observe(this, Observer { - listCharacters.setTagList("character", it) + binding.listCharacters.setTagList("character", it) }) viewModel.tags.observe(this, Observer { - listTags.setTagList("tag", it) + binding.listTags.setTagList("tag", it) }) viewModel.artist.observe(this, Observer { - listArtists.setTagList("artist", it) + binding.listArtists.setTagList("artist", it) }) viewModel.group.observe(this, Observer { - listGroups.setTagList("group", it) + binding.listGroups.setTagList("group", it) }) viewModel.language.observe(this, Observer { - listLanguages.setTagList("language", it) + binding.listLanguages.setTagList("language", it) }) viewModel.category.observe(this, Observer { - listCategories.setTagList("category", it) + binding.listCategories.setTagList("category", it) }) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorViewModel.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorViewModel.kt index 72b3427..e6afc45 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorViewModel.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/EditorViewModel.kt @@ -47,8 +47,8 @@ class EditorViewModel(application: Application) : AndroidViewModel(application) doujinDetailsDao = metadataRepo.doujinDetailsDao tagDao = metadataRepo.tagDao - tagsByCategory = Transformations.switchMap(selectedCategory) { category -> - return@switchMap tagDao.getByCategory(category) + tagsByCategory = selectedCategory.switchMap { category -> + tagDao.getByCategory(category) } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/TagBottomSheetDialog.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/TagBottomSheetDialog.kt index ae054cc..e6073d4 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/TagBottomSheetDialog.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/editor/TagBottomSheetDialog.kt @@ -11,13 +11,16 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.EditorNewTagAdapter +import com.flamyoad.tsukiviewer.databinding.EditorNewTagBottomsheetBinding import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import kotlinx.android.synthetic.main.editor_new_tag_bottomsheet.* class TagBottomSheetDialog() : BottomSheetDialogFragment() { + private var _binding: EditorNewTagBottomsheetBinding? = null + private val binding get() = _binding!! + private lateinit var viewmodel: EditorViewModel override fun onCreateView( @@ -25,7 +28,13 @@ class TagBottomSheetDialog() container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.editor_new_tag_bottomsheet, container, false) + _binding = EditorNewTagBottomsheetBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -34,7 +43,7 @@ class TagBottomSheetDialog() viewmodel = ViewModelProvider(requireActivity()).get(EditorViewModel::class.java) viewmodel.selectedCategory().observe(viewLifecycleOwner, Observer { category -> - lblCategory.text = category.capitalize() + binding.lblCategory.text = category.capitalize() }) initTagList() } @@ -45,14 +54,14 @@ class TagBottomSheetDialog() val linearLayoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) - listTags.adapter = adapter - listTags.layoutManager = linearLayoutManager + binding.listTags.adapter = adapter + binding.listTags.layoutManager = linearLayoutManager viewmodel.tagsByCategory.observe(this, Observer { adapter.setList(it) }) - inputEditText.addTextChangedListener(object : TextWatcher { + binding.inputEditText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} @@ -62,8 +71,8 @@ class TagBottomSheetDialog() } }) - btnInsertTag.setOnClickListener { - val tagName = inputEditText.text.toString() + binding.btnInsertTag.setOnClickListener { + val tagName = binding.inputEditText.text.toString() val category = viewmodel.selectedCategory().value!! listener.onTagCreated(tagName, category) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/fetcher/FetcherStatusActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/fetcher/FetcherStatusActivity.kt index 7b9ab84..c47332e 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/fetcher/FetcherStatusActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/fetcher/FetcherStatusActivity.kt @@ -12,11 +12,13 @@ import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.FetchHistoryAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityFetcherStatusBinding import com.flamyoad.tsukiviewer.network.FetchMetadataService -import kotlinx.android.synthetic.main.activity_fetcher_status.* class FetcherStatusActivity : AppCompatActivity() { + private lateinit var binding: ActivityFetcherStatusBinding + private var fetchService: FetchMetadataService? = null private var connection: ServiceConnection? = null @@ -25,7 +27,8 @@ class FetcherStatusActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_fetcher_status) + binding = ActivityFetcherStatusBinding.inflate(layoutInflater) + setContentView(binding.root) doBindService() } @@ -57,9 +60,9 @@ class FetcherStatusActivity : AppCompatActivity() { private fun initList() { adapter.setHasStableIds(true) - listItems.adapter = adapter - listItems.layoutManager = LinearLayoutManager(this) - listItems.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) + binding.listItems.adapter = adapter + binding.listItems.layoutManager = LinearLayoutManager(this) + binding.listItems.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) } private fun observeChanges() { @@ -68,11 +71,11 @@ class FetcherStatusActivity : AppCompatActivity() { if (service != null) { service.fetchPercentage.observe(this, Observer { if (it != null) { - txtProgress.text = it.getProgress() - txtPercentage.text = it.getPercentString() - progressBar.progress = it.getPercent() + binding.txtProgress.text = it.getProgress() + binding.txtPercentage.text = it.getPercentString() + binding.progressBar.progress = it.getPercent() - loadingIndicator.visibility = View.GONE + binding.loadingIndicator.visibility = View.GONE } }) @@ -85,11 +88,11 @@ class FetcherStatusActivity : AppCompatActivity() { If the new list has same reference as the previous list, the call is silently ignored by submitList() */ adapter.submitList(it.toList()) - txtProcessed.text = getString(R.string.processed_item_text, it.size) + binding.txtProcessed.text = getString(R.string.processed_item_text, it.size) }) service.currentItem().observe(this, Observer { - txtCurrentItem.text = it.absolutePath + binding.txtCurrentItem.text = it.absolutePath }) } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/bookmarks/BookmarkFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/bookmarks/BookmarkFragment.kt index fd91fad..71286eb 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/bookmarks/BookmarkFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/bookmarks/BookmarkFragment.kt @@ -25,7 +25,7 @@ import com.flamyoad.tsukiviewer.model.BookmarkGroup import com.flamyoad.tsukiviewer.model.BookmarkItem import com.flamyoad.tsukiviewer.model.ViewMode import com.flamyoad.tsukiviewer.utils.ui.GridItemDecoration -import kotlinx.android.synthetic.main.fragment_bookmark.* +import com.flamyoad.tsukiviewer.databinding.FragmentBookmarkBinding import java.util.* private const val ACTION_MODE = "action_mode" @@ -36,6 +36,9 @@ class BookmarkFragment : BaseFragment(), ActionModeListener, SearchView.OnQueryTextListener { + private var _binding: FragmentBookmarkBinding? = null + private val binding get() = _binding!! + private val viewModel: BookmarkViewModel by activityViewModels() private val groupAdapter = BookmarkGroupAdapter(this::onGroupChange, this::showNewGroupDialog) private val itemAdapter = BookmarkItemsAdapter(false) @@ -60,7 +63,8 @@ class BookmarkFragment : BaseFragment(), inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_bookmark, container, false) + _binding = FragmentBookmarkBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -73,6 +77,11 @@ class BookmarkFragment : BaseFragment(), itemAdapter.removeListener() } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) val isInActionMode = actionMode != null @@ -159,15 +168,15 @@ class BookmarkFragment : BaseFragment(), viewModel.bookmarkGroups().observe(viewLifecycleOwner, Observer { groupAdapter.submitList(it) - btnContextMenu.visibility = View.VISIBLE + binding.btnContextMenu.visibility = View.VISIBLE // If all bookmarks have been removed by user if (it.isNullOrEmpty()) { - header.text = "No bookmark exists" - btnContextMenu.visibility = View.GONE + binding.header.text = "No bookmark exists" + binding.btnContextMenu.visibility = View.GONE } else { if (viewModel.selectedGroupName.isNotBlank()) { - header.text = viewModel.selectedGroupName + binding.header.text = viewModel.selectedGroupName } } }) @@ -176,7 +185,7 @@ class BookmarkFragment : BaseFragment(), if (it == null) return@Observer viewModel.fetchBookmarkItems(it) - header.text = it.name + binding.header.text = it.name }) viewModel.bookmarkItems.observe(viewLifecycleOwner, Observer { @@ -199,9 +208,9 @@ class BookmarkFragment : BaseFragment(), val linearSnapHelper = LinearSnapHelper() - listGroups.adapter = groupAdapter - listGroups.layoutManager = layoutManager - linearSnapHelper.attachToRecyclerView(listGroups) + binding.listGroups.adapter = groupAdapter + binding.listGroups.layoutManager = layoutManager + linearSnapHelper.attachToRecyclerView(binding.listGroups) } private fun initBookmarkItems(viewMode: ViewMode) { @@ -227,20 +236,20 @@ class BookmarkFragment : BaseFragment(), val gridLayoutManager = GridLayoutManager(requireContext(), spanCount) - listItems.adapter = itemAdapter - listItems.layoutManager = gridLayoutManager - listItems.setHasFixedSize(true) - listItems.itemAnimator = null + binding.listItems.adapter = itemAdapter + binding.listItems.layoutManager = gridLayoutManager + binding.listItems.setHasFixedSize(true) + binding.listItems.itemAnimator = null // Prevent the same decor from stacking on top of each other. - if (listItems.itemDecorationCount == 0) { + if (binding.listItems.itemDecorationCount == 0) { val itemDecoration = GridItemDecoration( 2, 4, includeEdge = true ) - listItems.addItemDecoration(itemDecoration) + binding.listItems.addItemDecoration(itemDecoration) } viewModel.processedBookmarks().observe(viewLifecycleOwner, Observer { @@ -249,13 +258,13 @@ class BookmarkFragment : BaseFragment(), } private fun initUi() { - registerForContextMenu(btnContextMenu) - btnContextMenu.setOnClickListener { + registerForContextMenu(binding.btnContextMenu) + binding.btnContextMenu.setOnClickListener { it.showContextMenu() } - fab.setOnClickListener { - listItems.scrollToPosition(0) // Scrolls to top + binding.fab.setOnClickListener { + binding.listItems.scrollToPosition(0) // Scrolls to top } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CollectionFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CollectionFragment.kt index 2acae3c..6f853a8 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CollectionFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CollectionFragment.kt @@ -14,12 +14,15 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.BaseFragment import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.CollectionListAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentCollectionBinding import com.flamyoad.tsukiviewer.model.Collection -import kotlinx.android.synthetic.main.fragment_collection.* class CollectionFragment : BaseFragment(), SearchView.OnQueryTextListener { private val viewModel: CollectionViewModel by activityViewModels() + private var _binding: FragmentCollectionBinding? = null + private val binding get() = _binding!! + private var searchView: SearchView? = null private var previousSearchQuery: String = "" @@ -37,14 +40,19 @@ class CollectionFragment : BaseFragment(), SearchView.OnQueryTextListener { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_collection, container, false) + _binding = FragmentCollectionBinding.inflate(inflater, container, false) + return binding.root } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - fab.setOnClickListener { + binding.fab.setOnClickListener { val context = requireContext() val intent = Intent(context, CreateCollectionActivity::class.java) context.startActivity(intent) @@ -104,7 +112,7 @@ class CollectionFragment : BaseFragment(), SearchView.OnQueryTextListener { else -> 2 } - listCollections.apply { + binding.listCollections.apply { adapter = collectionAdapter layoutManager = GridLayoutManager(requireContext(), spanCount) setHasFixedSize(true) @@ -120,7 +128,7 @@ class CollectionFragment : BaseFragment(), SearchView.OnQueryTextListener { ) collectionAdapter.setHasStableIds(true) - listCollections.apply { + binding.listCollections.apply { layoutManager = LinearLayoutManager(requireContext()) adapter = collectionAdapter setHasFixedSize(true) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionActivity.kt index 8184332..3fc2317 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionActivity.kt @@ -13,25 +13,28 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.CollectionFilterDirectoryAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityCreateCollectionBinding import com.flamyoad.tsukiviewer.model.Collection import com.flamyoad.tsukiviewer.model.Tag import com.flamyoad.tsukiviewer.ui.search.TagSelectedListener import com.flamyoad.tsukiviewer.utils.extensions.toast import com.google.android.material.chip.Chip -import kotlinx.android.synthetic.main.activity_create_collection.* import java.io.File private const val REQUEST_DIR_PICKER = 101 class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { + private lateinit var binding: ActivityCreateCollectionBinding + private val viewModel: CreateCollectionViewModel by viewModels() private lateinit var dirAdapter: CollectionFilterDirectoryAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_create_collection) + binding = ActivityCreateCollectionBinding.inflate(layoutInflater) + setContentView(binding.root) val collectionId = intent.getLongExtra(COLLECTION_ID, -1L) viewModel.initCollectionData(collectionId) @@ -40,21 +43,21 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { initUi() viewModel.titles().observe(this, Observer { values -> - txtTitles.removeAllViews() + binding.txtTitles.removeAllViews() for (value in values) { insertTitle(value) } }) viewModel.currentCollection().observe(this, Observer { - fieldCollectionName.setText(it.name) + binding.fieldCollectionName.setText(it.name) if (it.minNumPages != Int.MIN_VALUE) { - fieldMinNumPages.setText(it.minNumPages.toString()) + binding.fieldMinNumPages.setText(it.minNumPages.toString()) } if (it.maxNumPages != Int.MAX_VALUE) { - fieldMaxNumPages.setText(it.maxNumPages.toString()) + binding.fieldMaxNumPages.setText(it.maxNumPages.toString()) } }) @@ -71,11 +74,11 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { }) viewModel.mustHaveAllIncludedTags().observe(this, Observer { - checkboxIncludedTags.isChecked = it + binding.checkboxIncludedTags.isChecked = it }) viewModel.mustHaveAllExcludedTags().observe(this, Observer { - checkboxExcludedTags.isChecked = it + binding.checkboxExcludedTags.isChecked = it }) } @@ -93,7 +96,7 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { } private fun initToolbar(collectionId: Long) { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.apply { setDisplayHomeAsUpEnabled(true) setDisplayShowHomeEnabled(true) @@ -107,26 +110,26 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { private fun initUi() { dirAdapter = CollectionFilterDirectoryAdapter(this::openDirectoryPicker, viewModel::removeDir) - listDirs.adapter = dirAdapter - listDirs.layoutManager = LinearLayoutManager(this) + binding.listDirs.adapter = dirAdapter + binding.listDirs.layoutManager = LinearLayoutManager(this) // Inflates default add button in the Included Tags - val includedTagNewChip = layoutInflater.inflate(R.layout.tag_list_add, parentLayout, false) as Chip + val includedTagNewChip = layoutInflater.inflate(R.layout.tag_list_add, binding.parentLayout, false) as Chip includedTagNewChip.text = "+" includedTagNewChip.setOnClickListener { openTagPicker(DialogTagPicker.Mode.Inclusive) } - listIncludedTags.addView(includedTagNewChip) + binding.listIncludedTags.addView(includedTagNewChip) // Inflates default add button in the Excluded Tags - val excludedTagNewChip = layoutInflater.inflate(R.layout.tag_list_add, parentLayout, false) as Chip + val excludedTagNewChip = layoutInflater.inflate(R.layout.tag_list_add, binding.parentLayout, false) as Chip excludedTagNewChip.text = "+" excludedTagNewChip.setOnClickListener { openTagPicker(DialogTagPicker.Mode.Exclusive) } - listExcludedTags.addView(excludedTagNewChip) + binding.listExcludedTags.addView(excludedTagNewChip) - fieldTitle.setOnEditorActionListener { textView, actionId, keyEvent -> + binding.fieldTitle.setOnEditorActionListener { textView, actionId, keyEvent -> if (actionId == EditorInfo.IME_ACTION_DONE) { viewModel.addTitle(textView.text.toString()) textView.text = "" @@ -136,59 +139,59 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { } // Disables the enter button of title if input is blank - fieldTitle.addTextChangedListener(object : TextWatcher { + binding.fieldTitle.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(str: CharSequence?, start: Int, before: Int, count: Int) { - btnAddTitle.isEnabled = !str.isNullOrBlank() + binding.btnAddTitle.isEnabled = !str.isNullOrBlank() } }) - fieldMinNumPages.addTextChangedListener(object : TextWatcher { + binding.fieldMinNumPages.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(str: CharSequence?, start: Int, before: Int, count: Int) { - val minNumPages = fieldMinNumPages.text.toString().toIntOrNull() ?: Int.MIN_VALUE - val maxNumPages = fieldMaxNumPages.text.toString().toIntOrNull() ?: Int.MAX_VALUE + val minNumPages = binding.fieldMinNumPages.text.toString().toIntOrNull() ?: Int.MIN_VALUE + val maxNumPages = binding.fieldMaxNumPages.text.toString().toIntOrNull() ?: Int.MAX_VALUE if (minNumPages > maxNumPages) { - layoutMinNumPages.error = "Invalid Input" + binding.layoutMinNumPages.error = "Invalid Input" } else { - layoutMinNumPages.isErrorEnabled = false - layoutMaxNumPages.isErrorEnabled = false + binding.layoutMinNumPages.isErrorEnabled = false + binding.layoutMaxNumPages.isErrorEnabled = false } } }) - fieldMaxNumPages.addTextChangedListener(object : TextWatcher { + binding.fieldMaxNumPages.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) {} override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(str: CharSequence?, start: Int, before: Int, count: Int) { - val minNumPages = fieldMinNumPages.text.toString().toIntOrNull() ?: Int.MIN_VALUE - val maxNumPages = fieldMaxNumPages.text.toString().toIntOrNull() ?: Int.MAX_VALUE + val minNumPages = binding.fieldMinNumPages.text.toString().toIntOrNull() ?: Int.MIN_VALUE + val maxNumPages = binding.fieldMaxNumPages.text.toString().toIntOrNull() ?: Int.MAX_VALUE if (minNumPages > maxNumPages) { - layoutMaxNumPages.error = "Invalid Input" + binding.layoutMaxNumPages.error = "Invalid Input" } else { - layoutMinNumPages.isErrorEnabled = false - layoutMaxNumPages.isErrorEnabled = false + binding.layoutMinNumPages.isErrorEnabled = false + binding.layoutMaxNumPages.isErrorEnabled = false } } }) - btnAddTitle.setOnClickListener { - viewModel.addTitle(fieldTitle.text.toString()) - fieldTitle.setText("") + binding.btnAddTitle.setOnClickListener { + viewModel.addTitle(binding.fieldTitle.text.toString()) + binding.fieldTitle.setText("") } - checkboxIncludedTags.setOnCheckedChangeListener { compoundButton, bool -> + binding.checkboxIncludedTags.setOnCheckedChangeListener { compoundButton, bool -> viewModel.setMustHaveAllIncludedTags(bool) } - checkboxExcludedTags.setOnCheckedChangeListener { compoundButton, bool -> + binding.checkboxExcludedTags.setOnCheckedChangeListener { compoundButton, bool -> viewModel.setMustHaveAllExcludedTags(bool) } } @@ -196,27 +199,27 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { private fun insertTitle(title: String) { if (title.isBlank()) return - val chip = layoutInflater.inflate(R.layout.tag_list_chip, parentLayout, false) as Chip + val chip = layoutInflater.inflate(R.layout.tag_list_chip, binding.parentLayout, false) as Chip chip.text = title chip.setOnCloseIconClickListener { viewModel.removeTitle(title) } - txtTitles.addView(chip) + binding.txtTitles.addView(chip) } private fun refreshIncludedTags(tags: List) { - listIncludedTags.removeAllViews() + binding.listIncludedTags.removeAllViews() // Inflates default add button in the Included Tags val includedTagNewChip = - layoutInflater.inflate(R.layout.tag_list_add, parentLayout, false) as Chip + layoutInflater.inflate(R.layout.tag_list_add, binding.parentLayout, false) as Chip includedTagNewChip.text = "+" includedTagNewChip.setOnClickListener { openTagPicker(DialogTagPicker.Mode.Inclusive) } - listIncludedTags.addView(includedTagNewChip) + binding.listIncludedTags.addView(includedTagNewChip) for (tag in tags) { val chip = layoutInflater.inflate(R.layout.tag_list_chip, null, false) as Chip @@ -225,21 +228,21 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { viewModel.removeIncludedTag(tag) } - listIncludedTags.addView(chip) + binding.listIncludedTags.addView(chip) } } private fun refreshExcludedTags(tags: List) { - listExcludedTags.removeAllViews() + binding.listExcludedTags.removeAllViews() // Inflates default add button in the Excluded Tags val excludedTagNewChip = - layoutInflater.inflate(R.layout.tag_list_add, parentLayout, false) as Chip + layoutInflater.inflate(R.layout.tag_list_add, binding.parentLayout, false) as Chip excludedTagNewChip.text = "+" excludedTagNewChip.setOnClickListener { openTagPicker(DialogTagPicker.Mode.Exclusive) } - listExcludedTags.addView(excludedTagNewChip) + binding.listExcludedTags.addView(excludedTagNewChip) for (tag in tags) { val chip = layoutInflater.inflate(R.layout.tag_list_chip, null, false) as Chip @@ -248,26 +251,26 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { viewModel.removeExcludedTag(tag) } - listExcludedTags.addView(chip) + binding.listExcludedTags.addView(chip) } } private fun submitCollection() { val existingCollectionId = intent.getLongExtra(COLLECTION_ID, -1) - val collectionName = fieldCollectionName.text.toString() + val collectionName = binding.fieldCollectionName.text.toString() if (collectionName.isBlank()) { toast("You must at least give it a name!") return } - val minNumPagesInput = fieldMinNumPages.text.toString() + val minNumPagesInput = binding.fieldMinNumPages.text.toString() val minNumPages = when (minNumPagesInput.isBlank()) { true -> Int.MIN_VALUE false -> minNumPagesInput.toInt() } - val maxNumPagesInput = fieldMaxNumPages.text.toString() + val maxNumPagesInput = binding.fieldMaxNumPages.text.toString() val maxNumPages = when (maxNumPagesInput.isBlank()) { true -> Int.MAX_VALUE false -> maxNumPagesInput.toInt() @@ -275,8 +278,8 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { if (minNumPages > maxNumPages) { toast("Invalid range of page numbers") - scrollView.postDelayed({ - scrollView.smoothScrollTo(0, layoutMetadata.bottom) + binding.scrollView.postDelayed({ + binding.scrollView.smoothScrollTo(0, binding.layoutMetadata.bottom) }, 100) return } @@ -291,8 +294,8 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { minNumPages = minNumPages, maxNumPages = maxNumPages, mustHaveAllTitles = false, // Hardcoded to use OR logic for now - mustHaveAllIncludedTags = checkboxIncludedTags.isChecked, - mustHaveAllExcludedTags = checkboxExcludedTags.isChecked + mustHaveAllIncludedTags = binding.checkboxIncludedTags.isChecked, + mustHaveAllExcludedTags = binding.checkboxExcludedTags.isChecked ) viewModel.submitCollection(collection) @@ -319,6 +322,9 @@ class CreateCollectionActivity : AppCompatActivity(), TagSelectedListener { DialogTagPicker.Mode.Exclusive -> { viewModel.addExcludedTag(tag) } + DialogTagPicker.Mode.None -> { + // Do nothing + } } // Dismisses the dialog once an item is clicked diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionViewModel.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionViewModel.kt index 1ea925b..ae72f0c 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionViewModel.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/CreateCollectionViewModel.kt @@ -57,8 +57,8 @@ class CreateCollectionViewModel(application: Application) : AndroidViewModel(app includedPathDao = db.includedFolderDao() includedPaths = includedPathDao.getAll() - tagList = Transformations.switchMap(tagQuery) { - return@switchMap tagRepo.getAllWithFilter(it) + tagList = tagQuery.switchMap { + tagRepo.getAllWithFilter(it) } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/DialogCollectionInfo.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/DialogCollectionInfo.kt index 258d191..48a7a34 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/DialogCollectionInfo.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/DialogCollectionInfo.kt @@ -15,9 +15,12 @@ import com.flamyoad.tsukiviewer.adapter.CollectionInfoTagAdapter import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.synthetic.main.dialog_collection_info.* +import com.flamyoad.tsukiviewer.databinding.DialogCollectionInfoBinding class DialogCollectionInfo : DialogFragment() { + private var _binding: DialogCollectionInfoBinding? = null + private val binding get() = _binding!! + private val viewModel: DialogCollectionInfoViewModel by activityViewModels() private val includedTagsAdapter = CollectionInfoTagAdapter(DialogTagPicker.Mode.Inclusive) @@ -30,7 +33,13 @@ class DialogCollectionInfo : DialogFragment() { savedInstanceState: Bundle? ): View? { dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) - return inflater.inflate(R.layout.dialog_collection_info, null, false) + _binding = DialogCollectionInfoBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onResume() { @@ -55,13 +64,13 @@ class DialogCollectionInfo : DialogFragment() { viewModel.initCollectionInfo(collectionId) viewModel.currentCollection().observe(this, Observer { - txtCollectionName.text = it.name - txtMinimumPages.text = when (it.minNumPages == Int.MIN_VALUE) { + binding.txtCollectionName.text = it.name + binding.txtMinimumPages.text = when (it.minNumPages == Int.MIN_VALUE) { true -> "Not specified" false -> it.minNumPages.toString() } - txtMaximumPages.text = when (it.maxNumPages == Int.MAX_VALUE) { + binding.txtMaximumPages.text = when (it.maxNumPages == Int.MAX_VALUE) { true -> "Not specified" false -> it.maxNumPages.toString() } @@ -69,13 +78,13 @@ class DialogCollectionInfo : DialogFragment() { viewModel.titles().observe(this, Observer { if (it.isEmpty()) { - txtTitles.text = "Not specified" + binding.txtTitles.text = "Not specified" } else { - txtTitles.text = it.joinToString(", ") { it } + binding.txtTitles.text = it.joinToString(", ") { it } } }) - listIncludedTags.apply { + binding.listIncludedTags.apply { layoutManager = FlexboxLayoutManager(this@DialogCollectionInfo.requireContext()).apply { flexDirection = FlexDirection.ROW flexWrap = FlexWrap.WRAP @@ -83,7 +92,7 @@ class DialogCollectionInfo : DialogFragment() { adapter = includedTagsAdapter } - listExcludedTags.apply { + binding.listExcludedTags.apply { layoutManager = FlexboxLayoutManager(this@DialogCollectionInfo.requireContext()).apply { flexDirection = FlexDirection.ROW flexWrap = FlexWrap.WRAP @@ -99,7 +108,7 @@ class DialogCollectionInfo : DialogFragment() { excludedTagsAdapter.setList(it) }) - listDirectories.apply { + binding.listDirectories.apply { adapter = dirAdapter layoutManager = LinearLayoutManager(requireContext()) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/doujins/CollectionDoujinsActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/doujins/CollectionDoujinsActivity.kt index 83d962b..e169405 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/doujins/CollectionDoujinsActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/collections/doujins/CollectionDoujinsActivity.kt @@ -20,6 +20,7 @@ import com.flamyoad.tsukiviewer.ActionModeListener import com.flamyoad.tsukiviewer.MyAppPreference import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.LocalDoujinsAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityCollectionDoujinsBinding import com.flamyoad.tsukiviewer.model.Doujin import com.flamyoad.tsukiviewer.model.ViewMode import com.flamyoad.tsukiviewer.ui.editor.EditorActivity @@ -27,7 +28,6 @@ import com.flamyoad.tsukiviewer.ui.home.collections.CollectionFragment import com.flamyoad.tsukiviewer.ui.home.collections.DialogCollectionInfo import com.flamyoad.tsukiviewer.utils.ui.GridItemDecoration import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.activity_collection_doujins.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -40,6 +40,8 @@ class CollectionDoujinsActivity : AppCompatActivity(), ActionModeListener, SearchView.OnQueryTextListener { + private lateinit var binding: ActivityCollectionDoujinsBinding + private val viewModel: CollectionDoujinsViewModel by viewModels() private val adapter = LocalDoujinsAdapter(this).apply { @@ -58,7 +60,8 @@ class CollectionDoujinsActivity : AppCompatActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_collection_doujins) + binding = ActivityCollectionDoujinsBinding.inflate(layoutInflater) + setContentView(binding.root) initToolbar() initRecyclerView(appPreference.getDoujinViewMode()) @@ -66,7 +69,7 @@ class CollectionDoujinsActivity : AppCompatActivity(), viewModel.snackbarText.observe(this, Observer { text -> if (text.isBlank()) return@Observer - Snackbar.make(parentLayout, text, Snackbar.LENGTH_LONG).show() + Snackbar.make(binding.parentLayout, text, Snackbar.LENGTH_LONG).show() viewModel.snackbarText.value = "" }) @@ -98,13 +101,13 @@ class CollectionDoujinsActivity : AppCompatActivity(), override fun onPrepareOptionsMenu(menu: Menu): Boolean { val progressBarItem = menu.findItem(R.id.progress_bar_loading) - val progressActionView = progressBarItem.actionView + val progressActionView = progressBarItem.actionView ?: return true val progressBar: ProgressBar = progressActionView.findViewById(R.id.progressBarSync) val searchItem: MenuItem? = menu.findItem(R.id.action_search) searchItem?.isVisible = false - val searchView = searchItem?.actionView as SearchView + val searchView = searchItem?.actionView as? SearchView ?: return true searchView.setOnQueryTextListener(this) viewModel.isLoading().observe(this, Observer { isLoading -> @@ -166,15 +169,15 @@ class CollectionDoujinsActivity : AppCompatActivity(), } private fun initToolbar() { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true) val collectionName = intent.getStringExtra(CollectionFragment.COLLECTION_NAME) val criterias = intent.getStringExtra(CollectionFragment.COLLECTION_CRITERIAS) - txtCollectionName.text = collectionName - txtCriterias.text = criterias + binding.txtCollectionName.text = collectionName + binding.txtCriterias.text = criterias } private fun initRecyclerView(viewMode: ViewMode) { @@ -200,10 +203,10 @@ class CollectionDoujinsActivity : AppCompatActivity(), val gridLayoutManager = GridLayoutManager(this, spanCount) - listDoujins.adapter = adapter - listDoujins.layoutManager = gridLayoutManager - listDoujins.setHasFixedSize(true) - listDoujins.itemAnimator = null + binding.listDoujins.adapter = adapter + binding.listDoujins.layoutManager = gridLayoutManager + binding.listDoujins.setHasFixedSize(true) + binding.listDoujins.itemAnimator = null val itemDecoration = GridItemDecoration( @@ -212,7 +215,7 @@ class CollectionDoujinsActivity : AppCompatActivity(), includeEdge = true ) - listDoujins.addItemDecoration(itemDecoration) + binding.listDoujins.addItemDecoration(itemDecoration) viewModel.searchedResult().observe(this, Observer { adapter.setList(it) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/local/LocalDoujinsFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/local/LocalDoujinsFragment.kt index 65f78ca..fded117 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/local/LocalDoujinsFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/local/LocalDoujinsFragment.kt @@ -19,6 +19,7 @@ import com.flamyoad.tsukiviewer.BaseFragment import com.flamyoad.tsukiviewer.MyAppPreference import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.LocalDoujinsAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentLocalDoujinsBinding import com.flamyoad.tsukiviewer.model.Doujin import com.flamyoad.tsukiviewer.model.Source import com.flamyoad.tsukiviewer.model.ViewMode @@ -27,7 +28,6 @@ import com.flamyoad.tsukiviewer.ui.home.collections.doujins.CollectionDoujinsAct import com.flamyoad.tsukiviewer.ui.search.SearchActivity import com.flamyoad.tsukiviewer.utils.ui.GridItemDecoration import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_local_doujins.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -45,6 +45,9 @@ class LocalDoujinsFragment : BaseFragment(), private val viewModel: LocalDoujinViewModel by activityViewModels() + private var _binding: FragmentLocalDoujinsBinding? = null + private val binding get() = _binding!! + private var appPreference: MyAppPreference? = null private var adapter = LocalDoujinsAdapter(this) @@ -62,8 +65,13 @@ class LocalDoujinsFragment : BaseFragment(), inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_local_doujins, container, false) + _binding = FragmentLocalDoujinsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -87,7 +95,7 @@ class LocalDoujinsFragment : BaseFragment(), super.onPrepareOptionsMenu(menu) val progressMenuItem = menu.findItem(R.id.progress_bar_sync) - val progressActionView = progressMenuItem.actionView + val progressActionView = progressMenuItem.actionView ?: return progressBar = progressActionView.findViewById(R.id.progressBarSync) progressBar.visibility = View.GONE @@ -100,7 +108,7 @@ class LocalDoujinsFragment : BaseFragment(), } else { progressBar.visibility = View.GONE sortMenuItem.isVisible = true - listLocalDoujins.visibility = View.VISIBLE + binding.listLocalDoujins.visibility = View.VISIBLE } }) } @@ -177,21 +185,21 @@ class LocalDoujinsFragment : BaseFragment(), viewModel.snackbarText.observe(viewLifecycleOwner, Observer { text -> if (text.isNullOrBlank()) return@Observer - Snackbar.make(rootView, text, Snackbar.LENGTH_LONG) + Snackbar.make(binding.rootView, text, Snackbar.LENGTH_LONG) .show() viewModel.snackbarText.value = "" }) viewModel.isSorting().observe(viewLifecycleOwner, Observer { stillSorting -> - if (stillSorting) { - listLocalDoujins.alpha = 0.6f - fastScroller.isEnabled = false - sortingIndicator.visibility = View.VISIBLE + if (stillSorting == true) { + binding.listLocalDoujins.alpha = 0.6f + binding.fastScroller.isEnabled = false + binding.sortingIndicator.root.visibility = View.VISIBLE } else { - listLocalDoujins.alpha = 1f - fastScroller.isEnabled = true - sortingIndicator.visibility = View.GONE + binding.listLocalDoujins.alpha = 1f + binding.fastScroller.isEnabled = true + binding.sortingIndicator.root.visibility = View.GONE } }) } @@ -220,27 +228,27 @@ class LocalDoujinsFragment : BaseFragment(), val gridLayoutManager = GridLayoutManager(context, spanCount) - listLocalDoujins.swapAdapter(adapter, false) - listLocalDoujins.layoutManager = gridLayoutManager + binding.listLocalDoujins.swapAdapter(adapter, false) + binding.listLocalDoujins.layoutManager = gridLayoutManager // Prevent the same decor from stacking on top of each other. - if (listLocalDoujins.itemDecorationCount == 0) { + if (binding.listLocalDoujins.itemDecorationCount == 0) { val itemDecoration = GridItemDecoration( spanCount, 10, includeEdge = true ) - listLocalDoujins.addItemDecoration(itemDecoration) + binding.listLocalDoujins.addItemDecoration(itemDecoration) } - listLocalDoujins.setHasFixedSize(true) - listLocalDoujins.itemAnimator = null + binding.listLocalDoujins.setHasFixedSize(true) + binding.listLocalDoujins.itemAnimator = null viewModel.doujinList().observe(viewLifecycleOwner, Observer { newList -> adapter.setList(newList) if (newList.isNotEmpty()) { - listLocalDoujins.visibility = View.VISIBLE + binding.listLocalDoujins.visibility = View.VISIBLE } }) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/DoujinTagsFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/DoujinTagsFragment.kt index 8238b44..7643276 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/DoujinTagsFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/DoujinTagsFragment.kt @@ -8,16 +8,19 @@ import androidx.lifecycle.Observer import com.flamyoad.tsukiviewer.BaseFragment import com.flamyoad.tsukiviewer.R +import com.flamyoad.tsukiviewer.databinding.FragmentDoujinTagsBinding import com.flamyoad.tsukiviewer.model.TagType import com.flamyoad.tsukiviewer.utils.extensions.reduceDragSensitivity import com.google.android.material.tabs.TabLayoutMediator -import kotlinx.android.synthetic.main.fragment_doujin_tags.* private const val SEARCH_VIEW = "search_view" class DoujinTagsFragment : BaseFragment(), SearchView.OnQueryTextListener { private val viewModel by activityViewModels() + private var _binding: FragmentDoujinTagsBinding? = null + private val binding get() = _binding!! + private var searchView: SearchView? = null private var previousSearchQuery: String = "" @@ -49,7 +52,13 @@ class DoujinTagsFragment : BaseFragment(), SearchView.OnQueryTextListener { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_doujin_tags, container, false) + _binding = FragmentDoujinTagsBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -75,14 +84,14 @@ class DoujinTagsFragment : BaseFragment(), SearchView.OnQueryTextListener { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - viewpager.adapter = TagFragmentAdapter(requireActivity(), tagList) + binding.viewpager.adapter = TagFragmentAdapter(requireActivity(), tagList) - TabLayoutMediator(tabLayout, viewpager, false, true) { tab, position -> + TabLayoutMediator(binding.tabLayout, binding.viewpager, false, true) { tab, position -> tab.text = tagList[position].toString() - viewpager.setCurrentItem(tab.position, true) + binding.viewpager.setCurrentItem(tab.position, true) }.attach() - viewpager.reduceDragSensitivity() + binding.viewpager.reduceDragSensitivity() viewModel.searchTerms().observe(viewLifecycleOwner, Observer { }) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/TagFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/TagFragment.kt index 1b0387b..182f941 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/TagFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/home/tags/TagFragment.kt @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.DoujinTagsAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentTagBinding import com.flamyoad.tsukiviewer.model.Doujin import com.flamyoad.tsukiviewer.model.Tag import com.flamyoad.tsukiviewer.model.TagType @@ -19,7 +20,6 @@ import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxItemDecoration import com.google.android.flexbox.FlexboxLayoutManager -import kotlinx.android.synthetic.main.fragment_tag.* private const val TAG_NAME = "tag_name" @@ -27,6 +27,9 @@ class TagFragment : Fragment() { private val viewModel by activityViewModels() + private var _binding: FragmentTagBinding? = null + private val binding get() = _binding!! + private val adapter = DoujinTagsAdapter(true) override fun onCreate(savedInstanceState: Bundle?) { @@ -38,7 +41,13 @@ class TagFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_tag, container, false) + _binding = FragmentTagBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -56,11 +65,11 @@ class TagFragment : Fragment() { } private fun initList() { - listTags.adapter = adapter + binding.listTags.adapter = adapter val linearLayoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false) - listTags.layoutManager = linearLayoutManager + binding.listTags.layoutManager = linearLayoutManager } private fun showDeleteDialog(tag: Tag) { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/HorizontalSwipeReaderFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/HorizontalSwipeReaderFragment.kt index e1fc6e5..c8ab910 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/HorizontalSwipeReaderFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/HorizontalSwipeReaderFragment.kt @@ -16,10 +16,13 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING import com.flamyoad.tsukiviewer.R +import com.flamyoad.tsukiviewer.databinding.FragmentHorizontalReaderBinding import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabViewModel -import kotlinx.android.synthetic.main.fragment_horizontal_reader.* class HorizontalSwipeReaderFragment : Fragment() { + private var _binding: FragmentHorizontalReaderBinding? = null + private val binding get() = _binding!! + private val viewModel: ReaderTabViewModel by viewModels( ownerProducer = { requireParentFragment() } ) @@ -36,16 +39,18 @@ class HorizontalSwipeReaderFragment : Fragment() { readerListener?.toggleBottomSheet(View.GONE) // Hides the bottom sheet when scrolling with volume button when (viewModel.volumeDownAction) { - VolumeButtonScrollDirection.GoToNextPage -> viewpager?.arrowScroll(View.FOCUS_RIGHT) - VolumeButtonScrollDirection.GoToPrevPage -> viewpager?.arrowScroll(View.FOCUS_LEFT) + VolumeButtonScrollDirection.GoToNextPage -> binding.viewpager.arrowScroll(View.FOCUS_RIGHT) + VolumeButtonScrollDirection.GoToPrevPage -> binding.viewpager.arrowScroll(View.FOCUS_LEFT) + VolumeButtonScrollDirection.Nothing -> { /* Do nothing */ } } } KeyEvent.KEYCODE_VOLUME_UP -> { readerListener?.toggleBottomSheet(View.GONE) when (viewModel.volumeUpAction) { - VolumeButtonScrollDirection.GoToNextPage -> viewpager?.arrowScroll(View.FOCUS_RIGHT) - VolumeButtonScrollDirection.GoToPrevPage -> viewpager?.arrowScroll(View.FOCUS_LEFT) + VolumeButtonScrollDirection.GoToNextPage -> binding.viewpager.arrowScroll(View.FOCUS_RIGHT) + VolumeButtonScrollDirection.GoToPrevPage -> binding.viewpager.arrowScroll(View.FOCUS_LEFT) + VolumeButtonScrollDirection.Nothing -> { /* Do nothing */ } } } } @@ -57,7 +62,8 @@ class HorizontalSwipeReaderFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_horizontal_reader, container, false) + _binding = FragmentHorizontalReaderBinding.inflate(inflater, container, false) + return binding.root } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -74,7 +80,7 @@ class HorizontalSwipeReaderFragment : Fragment() { viewModel.bottomThumbnailSelectedItem().observe(viewLifecycleOwner, Observer { if (it == -1) return@Observer - viewpager.setCurrentItem(it, false) + binding.viewpager.setCurrentItem(it, false) viewModel.resetBottomThumbnailState() }) } @@ -83,10 +89,11 @@ class HorizontalSwipeReaderFragment : Fragment() { super.onDestroyView() LocalBroadcastManager.getInstance(requireContext()) .unregisterReceiver(broadcastReceiver) + _binding = null } private fun initReader() { - viewpager.offscreenPageLimit = 1 + binding.viewpager.offscreenPageLimit = 1 val currentDir = arguments?.getString(CURRENT_DIR) ?: "" @@ -96,18 +103,18 @@ class HorizontalSwipeReaderFragment : Fragment() { } val imageAdapter = ImageFragmentStateAdapter(childFragmentManager) - viewpager.adapter = imageAdapter + binding.viewpager.adapter = imageAdapter viewModel.imageList().observe(viewLifecycleOwner, Observer { imageAdapter.setList(it) - viewpager.setCurrentItem(readerPosition, false) + binding.viewpager.setCurrentItem(readerPosition, false) readerListener?.onPageChange(readerPosition) viewModel.currentPath = currentDir }) - viewpager.addOnPageChangeListener(object: ViewPager.OnPageChangeListener { + binding.viewpager.addOnPageChangeListener(object: ViewPager.OnPageChangeListener { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { viewModel.currentScrolledPosition = position } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ImageFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ImageFragment.kt index a5162f0..c160ec1 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ImageFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ImageFragment.kt @@ -11,13 +11,16 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import com.davemorrissey.labs.subscaleview.ImageSource import com.flamyoad.tsukiviewer.R -import kotlinx.android.synthetic.main.reader_image_item.* +import com.flamyoad.tsukiviewer.databinding.ReaderImageItemBinding import java.io.File private const val IMAGE_PATH = "imagepath" class ImageFragment : Fragment() { + private var _binding: ReaderImageItemBinding? = null + private val binding get() = _binding!! + private var viewPagerListener: ViewPagerListener? = null override fun onCreateView( @@ -25,7 +28,8 @@ class ImageFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.reader_image_item, container, false) + _binding = ReaderImageItemBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -41,7 +45,7 @@ class ImageFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) // Only works for Horizontal image fragment. There is a separate method for Vertical image since its a RecyclerView - photoView.setOnTouchListener { view, motionEvent -> + binding.photoView.setOnTouchListener { view, motionEvent -> when (motionEvent.actionMasked) { MotionEvent.ACTION_DOWN -> { viewPagerListener?.setUserInputEnabled(false) @@ -59,20 +63,25 @@ class ImageFragment : Fragment() { override fun onResume() { super.onResume() - if (!photoView.isImageLoaded) { + if (!binding.photoView.isImageLoaded) { loadImage() } } override fun onStop() { super.onStop() - photoView.recycle() + binding.photoView.recycle() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } fun loadImage() { val imagePath = arguments?.getString(IMAGE_PATH) val image = File(imagePath) - photoView.setImage(ImageSource.uri(Uri.fromFile(image))) + binding.photoView.setImage(ImageSource.uri(Uri.fromFile(image))) } companion object { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ReaderActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ReaderActivity.kt index 3a46b0c..0c43243 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/ReaderActivity.kt @@ -15,13 +15,13 @@ import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.DoujinImagesAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityReaderBinding import com.flamyoad.tsukiviewer.model.RecentTab import com.flamyoad.tsukiviewer.ui.reader.recents.RecentTabsActivity import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabFragmentAdapter import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabListener import com.flamyoad.tsukiviewer.utils.extensions.reduceDragSensitivitySlightly import com.flamyoad.tsukiviewer.utils.extensions.toast -import kotlinx.android.synthetic.main.activity_reader.* const val MY_KEY_DOWN_INTENT = "my_key_down_intent" @@ -29,6 +29,8 @@ const val KEY_CODE = "key_code" class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener, LastReadPageNumberListener { + private lateinit var binding: ActivityReaderBinding + private val viewModel: ReaderViewModel by viewModels() private var positionFromImageGrid = 0 @@ -43,7 +45,8 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_reader) + binding = ActivityReaderBinding.inflate(layoutInflater) + setContentView(binding.root) window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) viewModel.directoryNoLongerExists().observe(this, Observer { notExists -> @@ -61,7 +64,7 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener viewModel.insertRecentTab(currentDir) setupViewPager() - viewPager.reduceDragSensitivitySlightly() + binding.viewPager.reduceDragSensitivitySlightly() } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -69,8 +72,8 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener return true } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { android.R.id.home -> { finish() } @@ -93,7 +96,7 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putInt(VIEWPAGER_INDEX, viewPager.currentItem) + outState.putInt(VIEWPAGER_INDEX, binding.viewPager.currentItem) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { @@ -128,7 +131,7 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener intent.getIntExtra(DoujinImagesAdapter.POSITION_BEFORE_OPENING_READER, 0) ) - viewPager.apply { + binding.viewPager.apply { adapter = tabFragmentAdapter } @@ -144,7 +147,7 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener switchReaderTab(currentTab) } - (viewPager.getChildAt(0) as RecyclerView).layoutManager!!.isItemPrefetchEnabled = false + (binding.viewPager.getChildAt(0) as RecyclerView).layoutManager!!.isItemPrefetchEnabled = false }) viewModel.currentTab().observe(this, Observer { @@ -154,13 +157,13 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener private fun switchReaderTab(tab: RecentTab) { if (viewPagerIndex != -1) { - viewPager.setCurrentItem(viewPagerIndex, false) + binding.viewPager.setCurrentItem(viewPagerIndex, false) viewPagerIndex = -1 // resets the index return } val position = tabFragmentAdapter.getTabPosition(tab.id ?: return) - viewPager.setCurrentItem(position, false) + binding.viewPager.setCurrentItem(position, false) } override fun onBackPressed() { @@ -174,7 +177,7 @@ class ReaderActivity : AppCompatActivity(), ViewPagerListener, ReaderTabListener } override fun setUserInputEnabled(isEnabled: Boolean) { - viewPager.isUserInputEnabled = isEnabled + binding.viewPager.isUserInputEnabled = isEnabled } override fun savePageNumber(pageNumber: Int) { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalStripReaderFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalStripReaderFragment.kt index 1658bbc..0cf44a0 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalStripReaderFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalStripReaderFragment.kt @@ -15,10 +15,13 @@ import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING import androidx.recyclerview.widget.WebtoonLayoutManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.ReaderImageAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentVerticalStripReaderBinding import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabViewModel -import kotlinx.android.synthetic.main.fragment_vertical_strip_reader.* class VerticalStripReaderFragment : Fragment() { + private var _binding: FragmentVerticalStripReaderBinding? = null + private val binding get() = _binding!! + private val viewModel: ReaderTabViewModel by viewModels( ownerProducer = { requireParentFragment() } ) @@ -50,7 +53,8 @@ class VerticalStripReaderFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_vertical_strip_reader, container, false) + _binding = FragmentVerticalStripReaderBinding.inflate(inflater, container, false) + return binding.root } override fun onResume() { @@ -71,6 +75,11 @@ class VerticalStripReaderFragment : Fragment() { .unregisterReceiver(broadcastReceiver) } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun onAttach(context: Context) { super.onAttach(context) viewPagerListener = context as ViewPagerListener @@ -96,7 +105,7 @@ class VerticalStripReaderFragment : Fragment() { initReader(readerPosition) setupPageIndicator(readerPosition) - listImages.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener { + binding.listImages.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener { override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { when (e.actionMasked) { MotionEvent.ACTION_DOWN -> { @@ -129,9 +138,9 @@ class VerticalStripReaderFragment : Fragment() { val imageAdapter = ReaderImageAdapter() layoutManager = WebtoonLayoutManager(requireActivity() as ReaderActivity) - listImages.adapter = imageAdapter - listImages.layoutManager = layoutManager - listImages.setHasFixedSize(true) + binding.listImages.adapter = imageAdapter + binding.listImages.layoutManager = layoutManager + binding.listImages.setHasFixedSize(true) viewModel.imageList().observe(viewLifecycleOwner, Observer { imageAdapter.setList(it) @@ -143,8 +152,8 @@ class VerticalStripReaderFragment : Fragment() { This is to solve the issue that, the image does not reload on screen rotation on my Xiaomi Note 4x but it does reload on my Zenfone (Device-speficic-bug?) */ - listImages.scrollBy(0, 1) - listImages.scrollBy(0, -1) + binding.listImages.scrollBy(0, 1) + binding.listImages.scrollBy(0, -1) readerListener?.onPageChange(readerPosition) @@ -155,7 +164,7 @@ class VerticalStripReaderFragment : Fragment() { private fun setupPageIndicator(readerPosition: Int) { readerListener?.onPageChange(readerPosition) - listImages.addOnScrollListener(object : RecyclerView.OnScrollListener() { + binding.listImages.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState == SCROLL_STATE_DRAGGING) { @@ -197,8 +206,10 @@ class VerticalStripReaderFragment : Fragment() { } VolumeButtonScrollMode.FixedDistance -> { - listImages?.scrollBy(0, viewModel.scrollDistance.unaryMinus()) + binding.listImages.scrollBy(0, viewModel.scrollDistance.unaryMinus()) } + + VolumeButtonScrollMode.Nothing -> { /* Do nothing */ } } } @@ -209,11 +220,14 @@ class VerticalStripReaderFragment : Fragment() { } VolumeButtonScrollMode.FixedDistance -> { - listImages?.scrollBy(0, viewModel.scrollDistance) + binding.listImages.scrollBy(0, viewModel.scrollDistance) } + + VolumeButtonScrollMode.Nothing -> { /* Do nothing */ } } } + VolumeButtonScrollDirection.Nothing -> { /* Do nothing */ } } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalSwipeReaderFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalSwipeReaderFragment.kt index 01c6557..7c8e5ab 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalSwipeReaderFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/VerticalSwipeReaderFragment.kt @@ -19,10 +19,13 @@ import androidx.viewpager2.widget.ViewPager2 import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.VerticalSwipeImageAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentVerticalSwipeReaderBinding import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabViewModel -import kotlinx.android.synthetic.main.fragment_vertical_swipe_reader.* class VerticalSwipeReaderFragment : Fragment() { + private var _binding: FragmentVerticalSwipeReaderBinding? = null + private val binding get() = _binding!! + private val viewModel: ReaderTabViewModel by viewModels( ownerProducer = { requireParentFragment() } ) @@ -39,16 +42,18 @@ class VerticalSwipeReaderFragment : Fragment() { readerListener?.toggleBottomSheet(View.GONE) // Hides the bottom sheet when scrolling with volume button when (viewModel.volumeDownAction) { - VolumeButtonScrollDirection.GoToNextPage -> viewpager.currentItem = viewpager.currentItem + 1 - VolumeButtonScrollDirection.GoToPrevPage -> viewpager.currentItem = viewpager.currentItem - 1 + VolumeButtonScrollDirection.GoToNextPage -> binding.viewpager.currentItem = binding.viewpager.currentItem + 1 + VolumeButtonScrollDirection.GoToPrevPage -> binding.viewpager.currentItem = binding.viewpager.currentItem - 1 + VolumeButtonScrollDirection.Nothing -> { /* Do nothing */ } } } KeyEvent.KEYCODE_VOLUME_UP -> { readerListener?.toggleBottomSheet(View.GONE) when (viewModel.volumeUpAction) { - VolumeButtonScrollDirection.GoToNextPage -> viewpager.currentItem = viewpager.currentItem + 1 - VolumeButtonScrollDirection.GoToPrevPage -> viewpager.currentItem = viewpager.currentItem - 1 + VolumeButtonScrollDirection.GoToNextPage -> binding.viewpager.currentItem = binding.viewpager.currentItem + 1 + VolumeButtonScrollDirection.GoToPrevPage -> binding.viewpager.currentItem = binding.viewpager.currentItem - 1 + VolumeButtonScrollDirection.Nothing -> { /* Do nothing */ } } } } @@ -60,7 +65,8 @@ class VerticalSwipeReaderFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_vertical_swipe_reader, container, false) + _binding = FragmentVerticalSwipeReaderBinding.inflate(inflater, container, false) + return binding.root } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -77,7 +83,7 @@ class VerticalSwipeReaderFragment : Fragment() { viewModel.bottomThumbnailSelectedItem().observe(viewLifecycleOwner, Observer { if (it == -1) return@Observer - viewpager.setCurrentItem(it, false) + binding.viewpager.setCurrentItem(it, false) viewModel.resetBottomThumbnailState() }) } @@ -86,10 +92,11 @@ class VerticalSwipeReaderFragment : Fragment() { super.onDestroyView() LocalBroadcastManager.getInstance(requireContext()) .unregisterReceiver(broadcastReceiver) + _binding = null } private fun initReader() { - viewpager.offscreenPageLimit = 1 + binding.viewpager.offscreenPageLimit = 1 val currentDir = arguments?.getString(CURRENT_DIR) ?: "" @@ -100,19 +107,19 @@ class VerticalSwipeReaderFragment : Fragment() { val imageAdapter = VerticalSwipeImageAdapter(this) - viewpager.orientation = ViewPager2.ORIENTATION_VERTICAL - viewpager.adapter = imageAdapter + binding.viewpager.orientation = ViewPager2.ORIENTATION_VERTICAL + binding.viewpager.adapter = imageAdapter viewModel.imageList().observe(viewLifecycleOwner, Observer { imageAdapter.setList(it) - viewpager.setCurrentItem(readerPosition, false) + binding.viewpager.setCurrentItem(readerPosition, false) readerListener?.onPageChange(readerPosition) viewModel.currentPath = currentDir }) - viewpager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() { + binding.viewpager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) viewModel.currentScrolledPosition = position diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/recents/RecentTabsActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/recents/RecentTabsActivity.kt index 19df456..791318c 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/recents/RecentTabsActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/recents/RecentTabsActivity.kt @@ -11,14 +11,16 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.RecentTabsAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityRecentTabsBinding import com.flamyoad.tsukiviewer.model.RecentTab import com.flamyoad.tsukiviewer.ui.reader.tabs.ReaderTabFragment import com.flamyoad.tsukiviewer.utils.extensions.toast import jp.wasabeef.recyclerview.animators.SlideInUpAnimator -import kotlinx.android.synthetic.main.activity_recent_tabs.* class RecentTabsActivity : AppCompatActivity() { + private lateinit var binding: ActivityRecentTabsBinding + private val viewModel: RecentTabsViewModel by viewModels() private var touchHelper: ItemTouchHelper? = null @@ -27,7 +29,8 @@ class RecentTabsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_recent_tabs) + binding = ActivityRecentTabsBinding.inflate(layoutInflater) + setContentView(binding.root) initReaderHistory() window.setFlags( @@ -35,11 +38,11 @@ class RecentTabsActivity : AppCompatActivity() { WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS ) - btnBack.setOnClickListener { + binding.btnBack.setOnClickListener { finish() } - btnClearRecents.setOnClickListener { + binding.btnClearRecents.setOnClickListener { val exemptedTab = intent.getLongExtra(ReaderTabFragment.TAB_ID, -1) viewModel.clearRecentTabs(exemptedTab) } @@ -70,18 +73,12 @@ class RecentTabsActivity : AppCompatActivity() { linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) - listTabs.apply { + binding.listTabs.apply { adapter = tabAdapter layoutManager = linearLayoutManager -// itemAnimator = SlideInUpAnimator().apply { -// addDuration = 150 -// removeDuration = 150 -// moveDuration = 100 -// changeDuration = 300 -// } } - listTabs.addOnScrollListener(object: RecyclerView.OnScrollListener() { + binding.listTabs.addOnScrollListener(object: RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) viewModel.hasScrolledList = true @@ -90,7 +87,7 @@ class RecentTabsActivity : AppCompatActivity() { val touchHelperCallback = RecentTabTouchHelperCallback(tabAdapter, currentTabId, viewModel::removeRecentTab) touchHelper = ItemTouchHelper(touchHelperCallback) - touchHelper?.attachToRecyclerView(listTabs) + touchHelper?.attachToRecyclerView(binding.listTabs) viewModel.tabList.observe(this, Observer { tabAdapter.submitList(it) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/tabs/ReaderTabFragment.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/tabs/ReaderTabFragment.kt index e41a51a..bbe6598 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/tabs/ReaderTabFragment.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/reader/tabs/ReaderTabFragment.kt @@ -17,12 +17,12 @@ import androidx.transition.Slide import androidx.transition.TransitionManager import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.BottomThumbnailAdapter +import com.flamyoad.tsukiviewer.databinding.FragmentReaderTabBinding import com.flamyoad.tsukiviewer.model.RecentTab import com.flamyoad.tsukiviewer.ui.reader.* import com.flamyoad.tsukiviewer.ui.reader.ReaderActivity.Companion.RECENT_TAB_REQUEST_CODE import com.flamyoad.tsukiviewer.ui.reader.recents.RecentTabsActivity import com.flamyoad.tsukiviewer.utils.extensions.toast -import kotlinx.android.synthetic.main.fragment_reader_tab.* import java.io.File private const val SWIPE_READER = "swipe_reader" @@ -31,6 +31,9 @@ class ReaderTabFragment : Fragment(), ReaderListener, BottomThumbnailAdapter.OnItemClickListener { + private var _binding: FragmentReaderTabBinding? = null + private val binding get() = _binding!! + private val viewModel: ReaderTabViewModel by viewModels() private val parentViewModel: ReaderViewModel by activityViewModels() @@ -50,7 +53,13 @@ class ReaderTabFragment : Fragment(), container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_reader_tab, container, false) + _binding = FragmentReaderTabBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun onAttach(context: Context) { @@ -78,7 +87,7 @@ class ReaderTabFragment : Fragment(), initToolbar() viewModel.recentTabs.observe(viewLifecycleOwner, Observer { - btnTab.text = it.size.toString() + binding.btnTab.text = it.size.toString() }) parentViewModel.readerMode().observe(viewLifecycleOwner, Observer { @@ -86,19 +95,19 @@ class ReaderTabFragment : Fragment(), setupSideMenu(it) }) - btnHorizontalSwipe.setOnClickListener { + binding.btnHorizontalSwipe.setOnClickListener { parentViewModel.setReaderMode(ReaderMode.HorizontalSwipe) } - btnVerticalSwipe.setOnClickListener { + binding.btnVerticalSwipe.setOnClickListener { parentViewModel.setReaderMode(ReaderMode.VerticalSwipe) } - btnVerticalStrip.setOnClickListener { + binding.btnVerticalStrip.setOnClickListener { parentViewModel.setReaderMode(ReaderMode.VerticalStrip) } - btnTab.setOnClickListener { + binding.btnTab.setOnClickListener { val tabId = arguments?.getLong(TAB_ID) ?: -1 val intent = Intent(requireContext(), RecentTabsActivity::class.java) intent.apply { @@ -107,7 +116,7 @@ class ReaderTabFragment : Fragment(), requireActivity().startActivityForResult(intent, RECENT_TAB_REQUEST_CODE) } - btnBack.setOnClickListener { + binding.btnBack.setOnClickListener { readerListener?.quitActivity() } @@ -136,7 +145,7 @@ class ReaderTabFragment : Fragment(), } if (mode == ReaderMode.HorizontalSwipe || mode == ReaderMode.VerticalSwipe) { - appBarLayout.setExpanded(true) + binding.appBarLayout.setExpanded(true) } childFragmentManager.beginTransaction() @@ -157,19 +166,19 @@ class ReaderTabFragment : Fragment(), when (mode) { ReaderMode.HorizontalSwipe -> { - btnHorizontalSwipe.background.setTint(activeBtnColor) - btnVerticalSwipe.background.setTint(inactiveBtnColor) - btnVerticalStrip.background.setTint(inactiveBtnColor) + binding.btnHorizontalSwipe.background.setTint(activeBtnColor) + binding.btnVerticalSwipe.background.setTint(inactiveBtnColor) + binding.btnVerticalStrip.background.setTint(inactiveBtnColor) } ReaderMode.VerticalSwipe -> { - btnHorizontalSwipe.background.setTint(inactiveBtnColor) - btnVerticalSwipe.background.setTint(activeBtnColor) - btnVerticalStrip.background.setTint(inactiveBtnColor) + binding.btnHorizontalSwipe.background.setTint(inactiveBtnColor) + binding.btnVerticalSwipe.background.setTint(activeBtnColor) + binding.btnVerticalStrip.background.setTint(inactiveBtnColor) } ReaderMode.VerticalStrip -> { - btnVerticalStrip.background.setTint(activeBtnColor) - btnVerticalSwipe.background.setTint(inactiveBtnColor) - btnHorizontalSwipe.background.setTint(inactiveBtnColor) + binding.btnVerticalStrip.background.setTint(activeBtnColor) + binding.btnVerticalSwipe.background.setTint(inactiveBtnColor) + binding.btnHorizontalSwipe.background.setTint(inactiveBtnColor) } } } @@ -182,16 +191,16 @@ class ReaderTabFragment : Fragment(), val snapHelper = LinearSnapHelper() - bottomListThumbnails.adapter = adapter - bottomListThumbnails.layoutManager = linearLayoutManager - snapHelper.attachToRecyclerView(bottomListThumbnails) + binding.bottomListThumbnails.adapter = adapter + binding.bottomListThumbnails.layoutManager = linearLayoutManager + snapHelper.attachToRecyclerView(binding.bottomListThumbnails) viewModel.imageList().observe(viewLifecycleOwner, Observer { adapter.setList(it) linearLayoutManager.scrollToPosition(0) }) - bottomListThumbnails.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener { + binding.bottomListThumbnails.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener { override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean { when (e.actionMasked) { MotionEvent.ACTION_DOWN -> { @@ -213,7 +222,7 @@ class ReaderTabFragment : Fragment(), } private fun initPageIndicator() { - bottomSheetOpener.setOnClickListener { + binding.bottomSheetOpener.setOnClickListener { toggleBottomSheet(View.VISIBLE) } } @@ -221,7 +230,7 @@ class ReaderTabFragment : Fragment(), private fun initToolbar() { val path = arguments?.getString(DIR_PATH) ?: "" val dir = File(path) - toolbarTitle.text = dir.name + binding.toolbarTitle.text = dir.name } override fun toggleBottomSheet(visibility: Int) { @@ -233,18 +242,18 @@ class ReaderTabFragment : Fragment(), addTarget(R.id.readerModeDialog) } - TransitionManager.beginDelayedTransition(bottomSheetDialog, btmSlide) - TransitionManager.beginDelayedTransition(readerModeDialog, rightSlide) + TransitionManager.beginDelayedTransition(binding.bottomSheetDialog, btmSlide) + TransitionManager.beginDelayedTransition(binding.readerModeDialog, rightSlide) - bottomSheetDialog.visibility = visibility - readerModeDialog.visibility = visibility + binding.bottomSheetDialog.visibility = visibility + binding.readerModeDialog.visibility = visibility } override fun onPageChange(pageNum: Int) { viewModel.currentScrolledPosition = pageNum setPageIndicatorNumber(pageNum + 1) - val bottomLayoutManager = bottomListThumbnails.layoutManager as LinearLayoutManager? + val bottomLayoutManager = binding.bottomListThumbnails.layoutManager as LinearLayoutManager? bottomLayoutManager?.scrollToPosition(pageNum) if (shouldReturnLastReadPosition) { @@ -255,7 +264,7 @@ class ReaderTabFragment : Fragment(), override fun onThumbnailClick(adapterPosition: Int) { viewModel.onThumbnailClick(adapterPosition) - val thumbnailLayoutManager = bottomListThumbnails.layoutManager as LinearLayoutManager? + val thumbnailLayoutManager = binding.bottomListThumbnails.layoutManager as LinearLayoutManager? thumbnailLayoutManager?.scrollToPosition(adapterPosition) hideReaderModeDialog() @@ -265,13 +274,13 @@ class ReaderTabFragment : Fragment(), val rightSlide = Slide(Gravity.END).apply { addTarget(R.id.readerModeDialog) } - TransitionManager.beginDelayedTransition(readerModeDialog, rightSlide) - readerModeDialog.visibility = View.GONE + TransitionManager.beginDelayedTransition(binding.readerModeDialog, rightSlide) + binding.readerModeDialog.visibility = View.GONE } private fun setPageIndicatorNumber(pageNum: Int) { val pageNumber = "Page: ${pageNum} / ${viewModel.getTotalImagesCount()}" - txtCurrentPageNumber.text = pageNumber + binding.txtCurrentPageNumber.text = pageNumber } companion object { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchActivity.kt index afc3df6..1a1e93f 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchActivity.kt @@ -14,14 +14,16 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.SearchHistoryAdapter +import com.flamyoad.tsukiviewer.databinding.ActivitySearchBinding import com.flamyoad.tsukiviewer.model.SearchHistory import com.flamyoad.tsukiviewer.model.Tag import com.flamyoad.tsukiviewer.utils.ActivityStackUtils import com.google.android.material.chip.Chip -import kotlinx.android.synthetic.main.activity_search.* -import kotlinx.android.synthetic.main.search_bar.* class SearchActivity : AppCompatActivity(), TagSelectedListener { + + private lateinit var binding: ActivitySearchBinding + private val viewModel: SearchViewModel by viewModels() companion object { @@ -35,7 +37,8 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_search) + binding = ActivitySearchBinding.inflate(layoutInflater) + setContentView(binding.root) savedInstanceState?.let { val tags = it.getStringArray(SELECTED_TAGS) @@ -47,11 +50,11 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { initSearchView() initSearchHistory() - btnSearch.setOnClickListener { + binding.btnSearch.setOnClickListener { submitSearch() } - chipAddItem.setOnClickListener { + binding.chipAddItem.setOnClickListener { val tagListFragment = TagPickerDialogFragment.newInstance() tagListFragment.show(supportFragmentManager, DIALOG_FRAGMENT_TAG) } @@ -59,7 +62,7 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - val selectedTags = chipGroup.children + val selectedTags = binding.chipGroup.children .map { x -> (x as Chip).text.toString() } .toList() .toTypedArray() @@ -74,7 +77,7 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { } private fun addChip(tagName: String) { - val chips = chipGroup.children as Sequence + val chips = binding.chipGroup.children as Sequence // Check for duplicates. If yes, then return for (chip in chips) { @@ -83,14 +86,14 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { } } - val chip = layoutInflater.inflate(R.layout.tag_list_chip, chipGroup, false) as Chip + val chip = layoutInflater.inflate(R.layout.tag_list_chip, binding.chipGroup, false) as Chip chip.text = tagName - chipGroup.addView(chip) + binding.chipGroup.addView(chip) revalidateCheckBox() chip.setOnCloseIconClickListener { - chipGroup.removeView(it) + binding.chipGroup.removeView(it) revalidateCheckBox() } } @@ -107,28 +110,28 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { // Hides the checkbox if the user did not choose any tags. Otherwise, show the checkbox private fun revalidateCheckBox() { - if (chipGroup.childCount > 1) - checkbox.visibility = View.VISIBLE + if (binding.chipGroup.childCount > 1) + binding.checkbox.visibility = View.VISIBLE else - checkbox.visibility = View.GONE + binding.checkbox.visibility = View.GONE } private fun submitSearch() { // Start from 1 because the first child is the "+" button val selectedTags = mutableListOf() - for (i in 1 until chipGroup.childCount) { - val chip = chipGroup.getChildAt(i) as Chip + for (i in 1 until binding.chipGroup.childCount) { + val chip = binding.chipGroup.getChildAt(i) as Chip selectedTags.add(chip.text.toString()) } - val title = searchView.query.toString() - val tags = selectedTags.joinToString(",") + val title: String = binding.searchBarInclude.searchView.query.toString() + val tags: String = selectedTags.joinToString(",") if (title.isBlank() && tags.isBlank()) { return } - val includeAllTags = checkbox.isChecked + val includeAllTags = binding.checkbox.isChecked val searchHistory = SearchHistory( title = title, @@ -147,7 +150,7 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { } private fun initSearchView() { - searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + binding.searchBarInclude.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { if (query != null && query.isNotBlank()) { val intent = Intent(this@SearchActivity, SearchResultActivity::class.java) @@ -198,9 +201,9 @@ class SearchActivity : AppCompatActivity(), TagSelectedListener { val itemDeco = DividerItemDecoration(this, LinearLayoutManager.VERTICAL) - listSearchHistory.adapter = adapter - listSearchHistory.layoutManager = layoutManager - listSearchHistory.addItemDecoration(itemDeco) + binding.listSearchHistory.adapter = adapter + binding.listSearchHistory.layoutManager = layoutManager + binding.listSearchHistory.addItemDecoration(itemDeco) viewModel.searchHistories.observe(this, Observer { adapter.submitList(it) diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchResultActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchResultActivity.kt index 24eaa5b..cb59afd 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchResultActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchResultActivity.kt @@ -20,6 +20,7 @@ import com.flamyoad.tsukiviewer.ActionModeListener import com.flamyoad.tsukiviewer.MyAppPreference import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.LocalDoujinsAdapter +import com.flamyoad.tsukiviewer.databinding.ActivitySearchResultBinding import com.flamyoad.tsukiviewer.model.Doujin import com.flamyoad.tsukiviewer.model.ViewMode import com.flamyoad.tsukiviewer.ui.editor.EditorActivity @@ -27,7 +28,6 @@ import com.flamyoad.tsukiviewer.utils.ActivityHistory import com.flamyoad.tsukiviewer.utils.ActivityStackUtils import com.flamyoad.tsukiviewer.utils.ui.GridItemDecoration import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.activity_search_result.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -40,6 +40,8 @@ class SearchResultActivity : AppCompatActivity(), ActionModeListener, SearchView.OnQueryTextListener { + private lateinit var binding: ActivitySearchResultBinding + private val viewModel: SearchResultViewModel by viewModels() private val adapter = LocalDoujinsAdapter(this, this::saveActivityInfo) @@ -55,14 +57,8 @@ class SearchResultActivity : AppCompatActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_search_result) - -// val resumePreviousActivity = intent.getBooleanExtra(ActivityStackUtils.RESUME_PREVIOUS_ACTIVITY, false) -// if (resumePreviousActivity) { -// overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right) -// } else { -// overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right) -// } + binding = ActivitySearchResultBinding.inflate(layoutInflater) + setContentView(binding.root) initRecyclerView(appPreference.getDoujinViewMode()) initToolbar() @@ -71,7 +67,7 @@ class SearchResultActivity : AppCompatActivity(), viewModel.snackbarText.observe(this, Observer { text -> if (text.isBlank()) return@Observer - Snackbar.make(parentLayout, text, Snackbar.LENGTH_LONG) + Snackbar.make(binding.parentLayout, text, Snackbar.LENGTH_LONG) .show() viewModel.snackbarText.value = "" @@ -135,13 +131,13 @@ class SearchResultActivity : AppCompatActivity(), */ override fun onPrepareOptionsMenu(menu: Menu): Boolean { val progressBarItem = menu.findItem(R.id.progress_bar_loading) - val progressActionView = progressBarItem.actionView + val progressActionView = progressBarItem.actionView ?: return true val progressBar: ProgressBar = progressActionView.findViewById(R.id.progressBarSync) val searchItem: MenuItem? = menu.findItem(R.id.action_search) searchItem?.isVisible = false - val searchView = searchItem?.actionView as SearchView + val searchView = searchItem?.actionView as? SearchView ?: return true searchView.setOnQueryTextListener(this) viewModel.isLoading().observe(this, Observer { isLoading -> @@ -193,7 +189,7 @@ class SearchResultActivity : AppCompatActivity(), } private fun initToolbar() { - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true) @@ -202,15 +198,15 @@ class SearchResultActivity : AppCompatActivity(), val tags = intent.getStringExtra(SearchActivity.SEARCH_TAGS) if (title.isNullOrBlank()) { - txtSearchTitle.text = "title: {}" + binding.txtSearchTitle.text = "title: {}" } else { - txtSearchTitle.text = getStringExtra(SearchActivity.SEARCH_TITLE) + binding.txtSearchTitle.text = getStringExtra(SearchActivity.SEARCH_TITLE) } if (tags.isNullOrBlank()) { - txtSearchTags.text = "tags: {}" + binding.txtSearchTags.text = "tags: {}" } else { - txtSearchTags.text = getStringExtra(SearchActivity.SEARCH_TAGS) + binding.txtSearchTags.text = getStringExtra(SearchActivity.SEARCH_TAGS) } } @@ -240,19 +236,19 @@ class SearchResultActivity : AppCompatActivity(), val gridLayoutManager = GridLayoutManager(this, spanCount) - listSearchedDoujins.adapter = adapter - listSearchedDoujins.layoutManager = gridLayoutManager - listSearchedDoujins.setHasFixedSize(true) - listSearchedDoujins.itemAnimator = null + binding.listSearchedDoujins.adapter = adapter + binding.listSearchedDoujins.layoutManager = gridLayoutManager + binding.listSearchedDoujins.setHasFixedSize(true) + binding.listSearchedDoujins.itemAnimator = null - if (listSearchedDoujins.itemDecorationCount == 0) { + if (binding.listSearchedDoujins.itemDecorationCount == 0) { val itemDecoration = GridItemDecoration( spanCount, 4, includeEdge = true ) - listSearchedDoujins.addItemDecoration(itemDecoration) + binding.listSearchedDoujins.addItemDecoration(itemDecoration) } viewModel.searchedResult().observe(this, Observer { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchViewModel.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchViewModel.kt index 5e6f5d1..7c714f9 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchViewModel.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/search/SearchViewModel.kt @@ -25,8 +25,8 @@ class SearchViewModel(application: Application) : AndroidViewModel(application) init { searchHistories = searchHistoryRepo.getAll(pageSize = 8) - tagList = Transformations.switchMap(tagQuery) { - return@switchMap tagRepo.getAllWithFilter(it) + tagList = tagQuery.switchMap { + tagRepo.getAllWithFilter(it) } } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/SettingsActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/SettingsActivity.kt index 690077e..1a68284 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/SettingsActivity.kt @@ -9,22 +9,25 @@ import androidx.lifecycle.Observer import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.flamyoad.tsukiviewer.R +import com.flamyoad.tsukiviewer.databinding.ActivitySettingsBinding import com.flamyoad.tsukiviewer.ui.settings.preferences.MainPreferences import com.flamyoad.tsukiviewer.utils.extensions.toast -import kotlinx.android.synthetic.main.activity_settings.* private const val TOOLBAR_TITLE = "settings_toolbar_title" class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback{ + private lateinit var binding: ActivitySettingsBinding + private val viewModel: SettingsViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_settings) + binding = ActivitySettingsBinding.inflate(layoutInflater) + setContentView(binding.root) - setSupportActionBar(toolbar) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) @@ -45,15 +48,19 @@ class SettingsActivity : AppCompatActivity(), viewModel.isRemovingItems().observe(this, Observer { isRemoving -> when (isRemoving) { true -> { - indicatorRemoveItems.visibility = View.VISIBLE - container.alpha = 0.6f + binding.indicatorRemoveItems.root.visibility = View.VISIBLE + binding.container.alpha = 0.6f } false -> { - indicatorRemoveItems.visibility = View.GONE - container.alpha = 1f + binding.indicatorRemoveItems.root.visibility = View.GONE + binding.container.alpha = 1f toast("Done") } + + null -> { + // Do nothing + } } }) } diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/includedfolders/IncludedFolderActivity.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/includedfolders/IncludedFolderActivity.kt index bd913d5..86e322f 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/includedfolders/IncludedFolderActivity.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/includedfolders/IncludedFolderActivity.kt @@ -1,11 +1,17 @@ package com.flamyoad.tsukiviewer.ui.settings.includedfolders import android.Manifest +import android.content.Intent +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.os.Environment +import android.provider.Settings import android.view.Menu import android.view.MenuItem import android.widget.Toast import androidx.activity.viewModels +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider @@ -14,25 +20,29 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.flamyoad.tsukiviewer.ui.settings.folderpicker.FolderPickerDialog import com.flamyoad.tsukiviewer.R import com.flamyoad.tsukiviewer.adapter.IncludedFolderAdapter +import com.flamyoad.tsukiviewer.databinding.ActivityIncludedFolderBinding import com.flamyoad.tsukiviewer.model.IncludedPath -import com.gun0912.tedpermission.PermissionListener -import com.gun0912.tedpermission.TedPermission -import kotlinx.android.synthetic.main.activity_included_folder.* +import com.gun0912.tedpermission.coroutine.TedPermission +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.io.File - class IncludedFolderActivity : AppCompatActivity(), AddFolderListener, RemoveFolderListener { + private lateinit var binding: ActivityIncludedFolderBinding + private val viewModel: IncludedFolderViewModel by viewModels() private lateinit var adapter: IncludedFolderAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_included_folder) - setSupportActionBar(toolbar) + binding = ActivityIncludedFolderBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) @@ -46,8 +56,8 @@ class IncludedFolderActivity : AppCompatActivity(), return true } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { R.id.action_add_folder -> { openFolderPicker() } @@ -57,16 +67,16 @@ class IncludedFolderActivity : AppCompatActivity(), private fun initRecyclerview() { adapter = IncludedFolderAdapter(this) - listDirectoryChosen.adapter = adapter + binding.listDirectoryChosen.adapter = adapter val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - listDirectoryChosen.layoutManager = linearLayoutManager + binding.listDirectoryChosen.layoutManager = linearLayoutManager val dividerItemDecoration = DividerItemDecoration( this, linearLayoutManager.orientation ) - listDirectoryChosen.addItemDecoration(dividerItemDecoration) + binding.listDirectoryChosen.addItemDecoration(dividerItemDecoration) viewModel.pathList.observe(this, Observer { adapter.setList(it) @@ -74,20 +84,36 @@ class IncludedFolderActivity : AppCompatActivity(), } private fun checkForPermission() { - val listener = object: PermissionListener { - override fun onPermissionGranted() { - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Android 11+ requires MANAGE_EXTERNAL_STORAGE + if (!Environment.isExternalStorageManager()) { + AlertDialog.Builder(this) + .setTitle("Storage Permission Required") + .setMessage("This app needs access to all files to browse your image folders. Please grant 'All files access' permission.") + .setPositiveButton("Grant") { _, _ -> + val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { + data = Uri.parse("package:$packageName") + } + startActivity(intent) + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + Toast.makeText(this, "Permission required to browse folders", Toast.LENGTH_SHORT).show() + } + .show() } - - override fun onPermissionDenied(deniedPermissions: MutableList?) { - Toast.makeText(this@IncludedFolderActivity, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show(); + } else { + // Android 10 and below - use runtime permissions + CoroutineScope(Dispatchers.Main).launch { + val permissionResult = TedPermission.create() + .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) + .check() + + if (!permissionResult.isGranted) { + Toast.makeText(this@IncludedFolderActivity, "Permission Denied\n" + permissionResult.deniedPermissions.toString(), Toast.LENGTH_SHORT).show() + } } } - - TedPermission.with(this) - .setPermissionListener(listener) - .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) - .check() } private fun openFolderPicker() { diff --git a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/preferences/AboutPreference.kt b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/preferences/AboutPreference.kt index 0cce145..60407ac 100644 --- a/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/preferences/AboutPreference.kt +++ b/app/src/main/java/com/flamyoad/tsukiviewer/ui/settings/preferences/AboutPreference.kt @@ -33,7 +33,8 @@ class AboutPreference: PreferenceFragmentCompat() { private fun getVersionNumber(): String { try { val manager = context?.packageManager - val info = manager?.getPackageInfo(context?.packageName, 0) + val packageName = context?.packageName ?: return "" + val info = manager?.getPackageInfo(packageName, 0) val versionName = info?.versionName return versionName ?: "" diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index ed19786..e0e78a0 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -8,7 +8,9 @@ android:orientation="vertical" tools:context=".ui.search.SearchActivity"> - +