Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ShowkaseBrowserFragment to be used as a replacement of ShowkaseBrowserActivity #351

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
package com.airbnb.android.showkasesample

import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.android.showkase.models.Showkase
import androidx.fragment.app.Fragment

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
startActivity(Showkase.getBrowserIntent(this))
finish()
setContentView(R.layout.main_activity)

replaceFragment(MainFragment.newInstance(), "MainFragment")
}

fun replaceFragment(fragment: Fragment, tag: String?) {
val fragmentManager = supportFragmentManager

val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)

transaction.addToBackStack(tag)
transaction.commit()
}

override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount > 0) {
supportFragmentManager.popBackStack()
} else {
super.onBackPressed()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.airbnb.android.showkasesample

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import com.airbnb.android.showkase.models.Showkase
import com.airbnb.android.showkase.ui.ShowkaseBrowserFragment

class MainFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val context = requireContext()
return ComposeView(context).apply {
setContent {
MainScreen(
navigateToShowkaseActivity = { startActivity(Showkase.getBrowserIntent(context)) },
navigateToShowkaseFragment = {
val fragment = ShowkaseBrowserFragment.newInstance(RootModule::class.java.name)
(activity as? MainActivity)?.replaceFragment(fragment, "ShowkaseBrowserFragment")
}
)
}
}
}

companion object {

fun newInstance(): MainFragment {
return MainFragment()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.airbnb.android.showkasesample

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun MainScreen(
navigateToShowkaseActivity: () -> Unit,
navigateToShowkaseFragment: () -> Unit,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = navigateToShowkaseActivity) {
Text(text = "Open Showkase activity")
}

Spacer(modifier = Modifier.height(16.dp))

Button(onClick = navigateToShowkaseFragment) {
Text(text = "Open Showkase fragment")
}
}
}
5 changes: 5 additions & 0 deletions sample/src/main/res/layout/main_activity.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.airbnb.android.showkase.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.airbnb.android.showkase.models.ShowkaseBrowserScreenMetadata
import com.airbnb.android.showkase.models.ShowkaseElementsMetadata
import com.airbnb.android.showkase.models.ShowkaseProvider

private const val AUTOGEN_CLASS_NAME = "Codegen"

/**
* composable entry point for Showkase
*
* @param classKey The name of the class with [@ShowkaseRoot] annotation
*
*/
@Composable
fun ShowkaseBrowser(
classKey: String,
) {
val (
groupedComponentsList,
groupedColorsList,
groupedTypographyList
) = getShowkaseProviderElements(classKey)

val showkaseBrowserScreenMetadata =
remember { mutableStateOf(ShowkaseBrowserScreenMetadata()) }
when {
groupedComponentsList.isNotEmpty() || groupedColorsList.isNotEmpty() ||
groupedTypographyList.isNotEmpty() -> {
ShowkaseBrowserApp(
groupedComponentsList.groupBy { it.group },
groupedColorsList.groupBy { it.colorGroup },
groupedTypographyList.groupBy { it.typographyGroup },
showkaseBrowserScreenMetadata
)
}

else -> {
ShowkaseErrorScreen(
errorText = "There were no elements that were annotated with either " +
"@ShowkaseComposable, @ShowkaseTypography or @ShowkaseColor. If " +
"you think this is a mistake, file an issue at " +
"https://github.com/airbnb/Showkase/issues"
)
}
}
}

private fun getShowkaseProviderElements(
classKey: String,
): ShowkaseElementsMetadata {
return try {
val showkaseComponentProvider =
Class.forName("$classKey${AUTOGEN_CLASS_NAME}").getDeclaredConstructor().newInstance()

val showkaseMetadata = (showkaseComponentProvider as ShowkaseProvider).metadata()

ShowkaseElementsMetadata(
componentList = showkaseMetadata.componentList,
colorList = showkaseMetadata.colorList,
typographyList = showkaseMetadata.typographyList
)
} catch (exception: ClassNotFoundException) {
ShowkaseElementsMetadata()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,85 +5,37 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.airbnb.android.showkase.exceptions.ShowkaseException
import com.airbnb.android.showkase.models.ShowkaseBrowserScreenMetadata
import com.airbnb.android.showkase.models.ShowkaseProvider
import com.airbnb.android.showkase.models.ShowkaseElementsMetadata

/**
* The activity that's responsible for showing all the UI elements that were annotated
* with the Showkase related annotations.
*/
class ShowkaseBrowserActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val classKey = intent.extras?.getString(SHOWKASE_ROOT_MODULE_KEY) ?: throw ShowkaseException(
"Missing key in bundle. Please start this activity by using the intent returned by " +
"the ShowkaseBrowserActivity.getIntent() method."
)
setContent {
val (
groupedComponentsList,
groupedColorsList,
groupedTypographyList
) = getShowkaseProviderElements(classKey)

val showkaseBrowserScreenMetadata =
remember { mutableStateOf(ShowkaseBrowserScreenMetadata()) }
when {
groupedComponentsList.isNotEmpty() || groupedColorsList.isNotEmpty() ||
groupedTypographyList.isNotEmpty() -> {
ShowkaseBrowserApp(
groupedComponentsList.groupBy { it.group },
groupedColorsList.groupBy { it.colorGroup },
groupedTypographyList.groupBy { it.typographyGroup },
showkaseBrowserScreenMetadata)
}
else -> {
ShowkaseErrorScreen(
errorText = "There were no elements that were annotated with either " +
"@ShowkaseComposable, @ShowkaseTypography or @ShowkaseColor. If " +
"you think this is a mistake, file an issue at " +
"https://github.com/airbnb/Showkase/issues"
)
}
}
}
}

private fun getShowkaseProviderElements(
classKey: String
): ShowkaseElementsMetadata {
return try {
val showkaseComponentProvider =
Class.forName("$classKey$AUTOGEN_CLASS_NAME").getDeclaredConstructor().newInstance()

val showkaseMetadata = (showkaseComponentProvider as ShowkaseProvider).metadata()

ShowkaseElementsMetadata(
componentList = showkaseMetadata.componentList,
colorList = showkaseMetadata.colorList,
typographyList = showkaseMetadata.typographyList
)
} catch (exception: ClassNotFoundException) {
ShowkaseElementsMetadata()
ShowkaseBrowser(classKey = classKey)
}
}

companion object {

private const val SHOWKASE_ROOT_MODULE_KEY = "SHOWKASE_ROOT_MODULE"
private const val AUTOGEN_CLASS_NAME = "Codegen"

/**
* Returns the intent that the users of this library need to use for starting the
* Showkase browser activity. Please make sure to use this instead of starting the
* activity directly as it sets the right value in the bundle in order for the activity
* to start correctly.
*
*
* @param context Android context
* @param rootModuleCanonicalName The canonical name of the implementation of
* @param rootModuleCanonicalName The canonical name of the implementation of
* ShowkaseRootModule.
*/
fun getIntent(context: Context, rootModuleCanonicalName: String) =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.airbnb.android.showkase.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import com.airbnb.android.showkase.exceptions.ShowkaseException

/**
* The activity that's responsible for showing all the UI elements that were annotated
* with the Showkase related annotations.
*/
class ShowkaseBrowserFragment : Fragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
val classKey = arguments?.getString(SHOWKASE_ROOT_MODULE_KEY) ?: throw ShowkaseException(
"Missing key in bundle. Please start this activity by using the intent returned by " +
"the ShowkaseBrowserActivity.getIntent() method."
)

return ComposeView(requireContext()).apply {
setContent {
ShowkaseBrowser(classKey = classKey)
}
}
}

companion object {

private const val SHOWKASE_ROOT_MODULE_KEY = "SHOWKASE_ROOT_MODULE"

/**
* Create a new instance of the ShowkaseBrowserFragment with the specified
* root module canonical name.
*
* @param rootModuleCanonicalName The canonical name of the implementation of
* ShowkaseRootModule.
* @return A new instance of ShowkaseBrowserFragment with the given arguments.
*/
fun newInstance(rootModuleCanonicalName: String): ShowkaseBrowserFragment {
val args = Bundle()
args.putString(SHOWKASE_ROOT_MODULE_KEY, rootModuleCanonicalName)
val fragment = ShowkaseBrowserFragment()
fragment.arguments = args
return fragment
}
}
}