Skip to content

Commit

Permalink
Merge branch 'release/2.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcel Vojtkovszky committed Dec 4, 2020
2 parents 58c4e00 + 11e2704 commit 5d5693f
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 72 deletions.
9 changes: 7 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
}

android {
Expand Down Expand Up @@ -30,6 +29,10 @@ android {
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

buildFeatures {
viewBinding true
}
}

dependencies {
Expand All @@ -40,7 +43,9 @@ dependencies {

implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'

testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import android.os.Bundle
import android.view.View
import com.vojtkovszky.singleactivitynavigation.BaseSingleActivity
import com.vojtkovszky.singleactivitynavigation.BaseSingleFragment
import com.vojtkovszky.singleactivitynavigation.FragmentType
import kotlinx.android.synthetic.main.activity_main.*
import com.vojtkovszky.singleactivitynavigationexample.databinding.ActivityMainBinding

class MainActivity : BaseSingleActivity() {

Expand All @@ -23,6 +22,8 @@ class MainActivity : BaseSingleActivity() {

private var selectedTabIndex = ROOT_FRAGMENT_POS_HOME

private lateinit var binding: ActivityMainBinding

// define main fragments based on position
override fun getNewRootFragmentInstance(positionIndex: Int): BaseSingleFragment? {
return when (positionIndex) {
Expand All @@ -35,10 +36,12 @@ class MainActivity : BaseSingleActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// we'll be switching main fragments with out bottom navigation
navigationView.setOnNavigationItemSelectedListener {
binding.navigationView.setOnNavigationItemSelectedListener {
selectedTabIndex = when (it.itemId) {
R.id.navigation_home -> ROOT_FRAGMENT_POS_HOME
R.id.navigation_dashboard -> ROOT_FRAGMENT_POS_DASHBOARD
Expand Down Expand Up @@ -89,6 +92,6 @@ class MainActivity : BaseSingleActivity() {

private fun handleNavigationViewVisibility(backStackCount: Int) {
// only make bottom bar visible if we're on root screen
navigationView.visibility = if (backStackCount == 0) View.VISIBLE else View.GONE
binding.navigationView.visibility = if (backStackCount == 0) View.VISIBLE else View.GONE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import android.view.View
import android.view.ViewGroup
import com.vojtkovszky.singleactivitynavigation.BaseSingleFragment
import com.vojtkovszky.singleactivitynavigation.FragmentType
import kotlinx.android.synthetic.main.fragment_main.*
import com.vojtkovszky.singleactivitynavigationexample.databinding.FragmentMainBinding

class MainFragment : BaseSingleFragment() {

private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
_binding = FragmentMainBinding.inflate(inflater, container, false)
return binding.root
}

override fun onResume() {
Expand All @@ -28,34 +32,39 @@ class MainFragment : BaseSingleFragment() {
})

// use it to set status text
textStatus.text = getString(R.string.this_is_type_fragment, title)
textStackSize.text = getString(R.string.stack_size_is,
binding.textStatus.text = getString(R.string.this_is_type_fragment, title)
binding.textStackSize.text = getString(R.string.stack_size_is,
activity?.supportFragmentManager?.backStackEntryCount ?: -1)
// and change title, but not needed in dialog
if (!fragmentType.isDialogOrBottomSheet()) {
baseSingleActivity?.supportActionBar?.title = title
}

// click listeners on buttons
buttonOpenRegular.let {
binding.buttonOpenRegular.let {
it.text = getString(R.string.open_type_fragment, getString(R.string.type_regular))
it.setOnClickListener { navigateTo(MainFragment()) }
}
buttonOpenModal.let {
binding.buttonOpenModal.let {
it.text = getString(R.string.open_type_fragment, getString(R.string.type_modal))
it.setOnClickListener { navigateTo(MainFragment(), openAsModal = true) }
}
buttonOpenBottomSheet.let {
binding.buttonOpenBottomSheet.let {
it.text = getString(R.string.open_type_fragment, getString(R.string.type_bottom_sheet))
it.setOnClickListener { openBottomSheet(MainFragment()) }
}
buttonOpenDialog.let {
binding.buttonOpenDialog.let {
it.text = getString(R.string.open_type_fragment, getString(R.string.type_dialog))
it.setOnClickListener { openDialog(MainFragment())}
}
buttonBackToRoot.let {
binding.buttonBackToRoot.let {
it.text = getString(R.string.back_to_root)
it.setOnClickListener { navigateBackToRoot() }
}
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
buildscript {
ext.kotlin_version = "1.4.10"
ext.kotlin_version = "1.4.20"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.0"
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
4 changes: 1 addition & 3 deletions singleactivitynavigation/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'maven-publish'
}

Expand All @@ -21,8 +20,7 @@ android {
defaultConfig {
minSdkVersion 17
targetSdkVersion 30
versionCode 1
versionName "2.1.0"
versionName "2.2.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ abstract class BaseSingleActivity: AppCompatActivity() {
fun openBottomSheet(fragment: BaseSingleFragment) {
closeCurrentlyOpenBottomSheet()
with(BaseSingleBottomSheetFragment()) {
this.fragment = fragment
this.fragment.addFragmentTypeToBundle(FragmentType.BOTTOM_SHEET)
this.fragment = fragment.also { it.addFragmentTypeToBundle(FragmentType.BOTTOM_SHEET) }
this.show(supportFragmentManager, fragment::class.simpleName)
}
}
Expand All @@ -190,8 +189,7 @@ abstract class BaseSingleActivity: AppCompatActivity() {
closeCurrentlyOpenDialog()
with(BaseSingleDialogFragment.newInstance(anchorView, useFullWidth)) {
setStyle(dialogStyle, dialogTheme)
this.fragment = fragment
this.fragment.addFragmentTypeToBundle(FragmentType.DIALOG)
this.fragment = fragment.also { it.addFragmentTypeToBundle(FragmentType.DIALOG) }
this.show(supportFragmentManager, fragment::class.simpleName)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,42 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
*/
internal class BaseSingleBottomSheetFragment: BottomSheetDialogFragment() {

// fragment is to be set before dialog is created.
// It will be attached to dialog's base view when the dialog is created.
// Don't rely on this instance as it will be lost when/if underlying activity is recreated.
// To retrieve fragment once attached, use getInnerFragment() instead
internal lateinit var fragment: BaseSingleFragment
// Fragment is to be set before dialog is created and will be attached to dialog's
// base view when the dialog is created. The reference will be removed immediately after.
internal var fragment: BaseSingleFragment? = null

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FrameLayout(requireActivity()).apply {
id = R.id.bottomSheetFragment
// fragment transaction is not needed if restoring from instance state as fragment
// Fragment transaction is not needed if restoring from instance state as fragment
// will be recreated in container.
if (savedInstanceState == null && ::fragment.isInitialized) {
if (savedInstanceState == null && fragment != null) {
childFragmentManager
.beginTransaction()
.replace(R.id.bottomSheetFragment, fragment, fragment::class.simpleName)
.replace(R.id.bottomSheetFragment, fragment!!, fragment!!::class.simpleName)
.runOnCommit {
// At this point fragment reference is not needed anymore and it's
// important we remove it to avoid memory leaks.
fragment = null
}
.commitAllowingStateLoss()
}
}
}

override fun onDestroyView() {
super.onDestroyView()

// remove fragment from view when dialog's view is getting destroyed
if (::fragment.isInitialized) {
childFragmentManager
.beginTransaction()
.remove(fragment)
.commitAllowingStateLoss()
}
}

override fun show(manager: FragmentManager, tag: String?) {
require(::fragment.isInitialized) {
require(fragment != null) {
"Fragment needs to be set before calling show"
}
super.show(manager, tag)

// only show if we can guarantee we won't get into IllegalStateException due to state loss commit
if (!manager.isDestroyed && !manager.isStateSaved) {
super.show(manager, tag)
}
}

/**
* Returns current child fragment attached to this fragment
* Returns current child [BaseSingleFragment] attached to this fragment
*/
internal fun getInnerFragment(): BaseSingleFragment? {
return childFragmentManager.fragments.filterIsInstance<BaseSingleFragment>().lastOrNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,9 @@ internal class BaseSingleDialogFragment: AppCompatDialogFragment() {
}
}

// fragment is to be set before dialog is created.
// It will be attached to dialog's base view when the dialog is created.
// Don't rely on this instance as it will be lost when/if underlying activity is recreated.
// To retrieve fragment once attached, use getInnerFragment() instead
internal lateinit var fragment: BaseSingleFragment
// Fragment is to be set before dialog is created and will be attached to dialog's
// base view when the dialog is created. The reference will be removed immediately after.
internal var fragment: BaseSingleFragment? = null

private var anchorHeight: Int = 0
private var anchorX: Float = 0f
Expand All @@ -56,15 +54,20 @@ internal class BaseSingleDialogFragment: AppCompatDialogFragment() {
}
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return FrameLayout(requireActivity()).apply {
id = R.id.dialogFragment
// fragment transaction is not needed if restoring from instance state as fragment
// will be recreated in container.
if (savedInstanceState == null && ::fragment.isInitialized) {
if (savedInstanceState == null && fragment != null) {
childFragmentManager
.beginTransaction()
.replace(R.id.dialogFragment, fragment, fragment::class.simpleName)
.replace(R.id.dialogFragment, fragment!!, fragment!!::class.simpleName)
.runOnCommit {
// At this point fragment reference is not needed anymore and it's
// important we remove it to avoid memory leaks.
fragment = null
}
.commitAllowingStateLoss()
}
}
Expand Down Expand Up @@ -97,27 +100,19 @@ internal class BaseSingleDialogFragment: AppCompatDialogFragment() {
}
}

override fun onDestroyView() {
super.onDestroyView()

// remove fragment from view when dialog's view is getting destroyed
if (::fragment.isInitialized) {
childFragmentManager
.beginTransaction()
.remove(fragment)
.commitAllowingStateLoss()
}
}

override fun show(manager: FragmentManager, tag: String?) {
require(::fragment.isInitialized) {
require(fragment != null) {
"Fragment or view needs to be set before calling show"
}
super.show(manager, tag)

// only show if we can guarantee we won't get into IllegalStateException due to state loss commit
if (!manager.isDestroyed && !manager.isStateSaved) {
super.show(manager, tag)
}
}

/**
* Returns current child fragment attached to this fragment
* Returns current child [BaseSingleFragment] attached to this fragment
*/
internal fun getInnerFragment(): BaseSingleFragment? {
return childFragmentManager.fragments.filterIsInstance<BaseSingleFragment>().lastOrNull()
Expand Down

0 comments on commit 5d5693f

Please sign in to comment.