diff --git a/app/build.gradle b/app/build.gradle index c5cf1b8..dbd1112 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ kotlin { } android { - compileSdk 34 + compileSdk 36 namespace "otus.gpb.homework.fragments" defaultConfig { @@ -21,6 +21,10 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + buildFeatures { + viewBinding true + } + buildTypes { release { minifyEnabled false @@ -42,6 +46,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.activity:activity:1.11.0' + implementation 'androidx.fragment:fragment-ktx:1.8.9' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9604b34..4ccebbd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,15 @@ android:supportsRtl="true" android:theme="@style/Theme.Fragments" tools:targetApi="31"> + + + diff --git a/app/src/main/java/otus/gpb/homework/fragments/ExchangeDataExt.kt b/app/src/main/java/otus/gpb/homework/fragments/ExchangeDataExt.kt new file mode 100644 index 0000000..f3122f1 --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/ExchangeDataExt.kt @@ -0,0 +1,31 @@ +package otus.gpb.homework.fragments + +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import androidx.fragment.app.setFragmentResult +import androidx.fragment.app.setFragmentResultListener + +const val COLOR_REQUEST_KEY = "request_key" +const val COLOR_RESULT_KEY = "result_key" + +fun Fragment.sendResult(data: Int, isTablet: Boolean = false) { + if (isTablet) { + requireActivity().supportFragmentManager.setFragmentResult(COLOR_REQUEST_KEY, bundleOf(COLOR_RESULT_KEY to data)) + } else { + setFragmentResult(COLOR_REQUEST_KEY, bundleOf(COLOR_RESULT_KEY to data)) + } +} + +fun Fragment.observeResult(isTablet: Boolean = false, callback: (Int) -> Unit) { + if (isTablet) { + requireActivity().supportFragmentManager.setFragmentResultListener(COLOR_REQUEST_KEY, viewLifecycleOwner) { _, bundle -> + val data = bundle.getInt(COLOR_RESULT_KEY) + callback(data) + } + } else { + setFragmentResultListener(COLOR_REQUEST_KEY) { _, bundle -> + val data = bundle.getInt(COLOR_RESULT_KEY) + callback(data) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FirstActivity.kt b/app/src/main/java/otus/gpb/homework/fragments/FirstActivity.kt new file mode 100644 index 0000000..6b40597 --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FirstActivity.kt @@ -0,0 +1,27 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import otus.gpb.homework.fragments.databinding.ActivityFirstBinding + +class FirstActivity : AppCompatActivity() { + private var binding: ActivityFirstBinding? = null + private lateinit var fragmentA: FragmentA + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityFirstBinding.inflate(layoutInflater) + setContentView(binding?.root) + + if (savedInstanceState == null) { + fragmentA = FragmentA() + } else { + fragmentA = supportFragmentManager.findFragmentByTag("FragmentA") as FragmentA + } + + supportFragmentManager.beginTransaction() + .replace(R.id.fragmentA_container, fragmentA, "FragmentA") + .addToBackStack(null) + .commit() + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentA.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentA.kt new file mode 100644 index 0000000..d1baa2e --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentA.kt @@ -0,0 +1,82 @@ +package otus.gpb.homework.fragments + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.core.view.isVisible +import otus.gpb.homework.fragments.databinding.FragmentABinding + +class FragmentA : Fragment() { + private var _binding: FragmentABinding? = null + val binding get() = _binding!! + + override fun onAttach(context: Context) { + super.onAttach(context) + + requireActivity().onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val count = childFragmentManager.backStackEntryCount + Toast.makeText(requireContext(), "BackStack count: $count", Toast.LENGTH_SHORT) + .show() + when { + count > 1 -> childFragmentManager.popBackStack() + count == 1 -> { + binding.buttonA.visibility = View.VISIBLE + binding.textViewA.visibility = View.VISIBLE + + childFragmentManager.popBackStack() + } + count == 0 -> requireActivity().finish() + else -> { + isEnabled = false + requireActivity().onBackPressedDispatcher.onBackPressed() + } + } + } + }) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentABinding.inflate(inflater, container, false) + val view = binding.root + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.buttonA.setOnClickListener { + openFragmentAA() + } + } + + private fun openFragmentAA() { + val fragmentAA = FragmentAA() + + sendResult(ColorGenerator.generateColor()) + + childFragmentManager.beginTransaction() + .replace(R.id.containerA, fragmentAA) + .addToBackStack(null) + .commit() + + binding.buttonA.visibility = View.GONE + binding.textViewA.visibility = View.GONE + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentAA.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentAA.kt new file mode 100644 index 0000000..a98b6a7 --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentAA.kt @@ -0,0 +1,52 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.os.bundleOf +import otus.gpb.homework.fragments.databinding.FragmentAaBinding + +class FragmentAA : Fragment() { + private var _binding: FragmentAaBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAaBinding.inflate(inflater, container, false) + val view = binding.root + observeResult(true) { color -> + binding.containerAA.setBackgroundColor(color) + } + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.buttonAB.setOnClickListener { + openFragmentAB() + } + } + + private fun openFragmentAB() { + val fragmentAB = FragmentAB() + val color = ColorGenerator.generateColor() + fragmentAB.arguments = bundleOf("color" to color) + childFragmentManager.beginTransaction() + .replace(R.id.containerAA, fragmentAB) + .addToBackStack(null) + .commit() + + binding.buttonAB.visibility = View.GONE + binding.textViewB.visibility = View.GONE + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentAB.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentAB.kt new file mode 100644 index 0000000..9e4141c --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentAB.kt @@ -0,0 +1,29 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import otus.gpb.homework.fragments.databinding.FragmentAbBinding + +class FragmentAB : Fragment() { + private var _binding: FragmentAbBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentAbBinding.inflate(inflater, container, false) + val view = binding.root + val color = arguments?.getInt("color") ?: 0 + binding.containerAB.setBackgroundColor(color) + return view + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentB.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentB.kt new file mode 100644 index 0000000..b5e2a0d --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentB.kt @@ -0,0 +1,27 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import otus.gpb.homework.fragments.databinding.FragmentBBinding + +class FragmentB : Fragment() { + private var _binding: FragmentBBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentBBinding.inflate(inflater, container, false) + val view = binding.root + return view + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentBA.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentBA.kt new file mode 100644 index 0000000..d2d3e0b --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentBA.kt @@ -0,0 +1,85 @@ +package otus.gpb.homework.fragments + +import android.content.Context +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.setFragmentResultListener +import otus.gpb.homework.fragments.databinding.FragmentBABinding + +class FragmentBA : Fragment() { + private var _binding: FragmentBABinding? = null + private val binding get() = _binding!! + private var isTablet = false + + override fun onAttach(context: Context) { + super.onAttach(context) + + requireActivity().onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val count = childFragmentManager.backStackEntryCount + Toast.makeText(requireContext(), "BackStack count: $count", Toast.LENGTH_SHORT) + .show() + when { + count > 1 -> childFragmentManager.popBackStack() + count == 1 -> { + binding.buttonBA.visibility = View.VISIBLE + childFragmentManager.popBackStack() + } + + count == 0 -> requireActivity().finish() + else -> { + isEnabled = false + requireActivity().onBackPressedDispatcher.onBackPressed() + } + } + } + }) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentBABinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + isTablet = requireActivity().findViewById(R.id.fragment1) != null + + observeResult(isTablet) { color -> + binding.containerBA.setBackgroundColor(color) + } + + if (isTablet) { + binding.buttonBA.visibility = View.GONE + } + + binding.buttonBA.setOnClickListener { + openFragmentBB() + } + } + + private fun openFragmentBB() { + val fragmentBB = FragmentBB() + parentFragmentManager.beginTransaction() + .replace(R.id.containerBA, fragmentBB, "FragmentBB") + .addToBackStack(null) + .commit() + + binding.buttonBA.visibility = View.GONE + } + + fun showButtonBA() { + binding.buttonBA.visibility = View.VISIBLE + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/FragmentBB.kt b/app/src/main/java/otus/gpb/homework/fragments/FragmentBB.kt new file mode 100644 index 0000000..ed75fb1 --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/FragmentBB.kt @@ -0,0 +1,52 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import otus.gpb.homework.fragments.databinding.FragmentBBBinding + +class FragmentBB : Fragment() { + private var _binding: FragmentBBBinding? = null + private val binding get() = _binding!! + private var isTablet = false + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentBBBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + isTablet = requireActivity().findViewById(R.id.fragment1) != null + + binding.buttonSendColorToBA.setOnClickListener { + val color = ColorGenerator.generateColor() + val count = childFragmentManager.backStackEntryCount + Toast.makeText(requireContext(), "BackStack count: $count", Toast.LENGTH_SHORT) + .show() + if (isTablet) { + sendResult(color, true) + } else { + sendResult(color) + parentFragmentManager.popBackStack() + } + } + } + + override fun onDestroyView() { + super.onDestroyView() + if (!isTablet) { + val fragmentBA = requireActivity().supportFragmentManager.findFragmentByTag("FragmentBA") as? FragmentBA + fragmentBA?.showButtonBA() + } + } +} + + diff --git a/app/src/main/java/otus/gpb/homework/fragments/MainActivity.kt b/app/src/main/java/otus/gpb/homework/fragments/MainActivity.kt index 5e89c44..e6e655b 100644 --- a/app/src/main/java/otus/gpb/homework/fragments/MainActivity.kt +++ b/app/src/main/java/otus/gpb/homework/fragments/MainActivity.kt @@ -1,12 +1,27 @@ package otus.gpb.homework.fragments +import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import otus.gpb.homework.fragments.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { + private var binding: ActivityMainBinding? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding?.root) + + binding?.buttonOpenFragmentA?.setOnClickListener { + startActivity(Intent(this, FirstActivity::class.java)) + } + binding?.buttonOpenFragmentB?.setOnClickListener { + startActivity(Intent(this, SecondActivity::class.java)) + } + binding?.buttonOpenFragmentC?.setOnClickListener { + startActivity(Intent(this, ThirdActivity::class.java)) + } } + } \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/SecondActivity.kt b/app/src/main/java/otus/gpb/homework/fragments/SecondActivity.kt new file mode 100644 index 0000000..1e39e8c --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/SecondActivity.kt @@ -0,0 +1,25 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import otus.gpb.homework.fragments.databinding.ActivitySecondBinding + +class SecondActivity : AppCompatActivity() { + private var binding: ActivitySecondBinding? = null + private lateinit var fragmentB: FragmentB + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivitySecondBinding.inflate(layoutInflater) + setContentView(binding?.root) + + if (savedInstanceState == null) { + fragmentB = FragmentB() + } else { + } + + supportFragmentManager.beginTransaction() + .replace(R.id.fragmentB_container, fragmentB) + .commit() + } +} \ No newline at end of file diff --git a/app/src/main/java/otus/gpb/homework/fragments/ThirdActivity.kt b/app/src/main/java/otus/gpb/homework/fragments/ThirdActivity.kt new file mode 100644 index 0000000..2e2f1eb --- /dev/null +++ b/app/src/main/java/otus/gpb/homework/fragments/ThirdActivity.kt @@ -0,0 +1,50 @@ +package otus.gpb.homework.fragments + +import android.os.Bundle +import android.widget.Toast +import androidx.fragment.app.FragmentActivity +import otus.gpb.homework.fragments.databinding.ActivityThirdBinding + +class ThirdActivity : FragmentActivity() { + private var binding: ActivityThirdBinding? = null + private lateinit var fragmentBA: FragmentBA + private lateinit var fragmentBB: FragmentBB + private var isTablet = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityThirdBinding.inflate(layoutInflater) + setContentView(binding?.root) + + determinePaneLayout() + + fragmentBA = supportFragmentManager.findFragmentByTag("FragmentBA") as? FragmentBA ?: FragmentBA() + fragmentBB = supportFragmentManager.findFragmentByTag("FragmentBB") as? FragmentBB ?: FragmentBB() + + if (isTablet) { + if (supportFragmentManager.findFragmentByTag("FragmentBB")?.view?.parent == null) { + supportFragmentManager.beginTransaction() + .replace(R.id.fragment1, fragmentBB, "FragmentBB") + .commit() + } + if (supportFragmentManager.findFragmentByTag("FragmentBA")?.view?.parent == null) { + supportFragmentManager.beginTransaction() + .replace(R.id.containerBA, fragmentBA, "FragmentBA") + .commit() + } + } else { + if (supportFragmentManager.findFragmentByTag("FragmentBA")?.view?.parent == null) { + supportFragmentManager.beginTransaction() + .replace(R.id.fragmentC_container, fragmentBA, "FragmentBA") + .commit() + } + } + } + + private fun determinePaneLayout() { + val containerBA = binding?.root?.findViewById(R.id.containerBA) + val fragment1 = binding?.root?.findViewById(R.id.fragment1) + isTablet = containerBA != null && fragment1 != null + Toast.makeText(this, "isTablet: $isTablet", Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_third.xml b/app/src/main/res/layout-land/activity_third.xml new file mode 100644 index 0000000..ff11a02 --- /dev/null +++ b/app/src/main/res/layout-land/activity_third.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout-sw600dp/activity_third.xml b/app/src/main/res/layout-sw600dp/activity_third.xml new file mode 100644 index 0000000..3fc5318 --- /dev/null +++ b/app/src/main/res/layout-sw600dp/activity_third.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_first.xml b/app/src/main/res/layout/activity_first.xml new file mode 100644 index 0000000..9906d7b --- /dev/null +++ b/app/src/main/res/layout/activity_first.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 17eab17..ecdcaea 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,31 @@ - - + android:layout_gravity="center" + android:text="Open Fragment A" /> - \ No newline at end of file +