Android-stories
A simple stories library inspired by Instagram and alike.
Requirements
- Min SDK >= 22
Installation
Add these dependencies to your project:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.redmadrobot-tomsk:android-stories:1.1.1'
}
Initialization
- Initialize StoriesCacheFactory in your
Application
class.
class App : Application() {
override fun onCreate() {
super.onCreate()
StoriesCacheFactory
.init(this, CoroutineScope(Dispatchers.IO + SupervisorJob()))
.preloadImages(false)
.setConfig(StoriesConfig.All)
}
}
- Add internet permission to your manifest for Glide.
Setting up stories activity
- Extend
StoriesBaseActivity
and override its functions how you see fit. Note that you MUST start stories activity with intent returned byStoriesBaseActivity.newStoriesIntent
because of required parameters (StoriesInputParams
). Otherwise, exception will be thrown.
class StoriesActivity : StoriesBaseActivity() {
companion object {
fun newIntent(
context: Context,
storiesInputParams: StoriesInputParams
): Intent = newStoriesIntent(
context = context,
clazz = StoriesActivity::class.java,
storiesInputParams = storiesInputParams
)
}
override fun onStoryActionClicked(url: String) {
// TODO: Your implementation.
}
override fun closeStories() {
super.closeStories()
}
override fun onCompleteStory() {
super.onCompleteStory()
}
override fun hasPreviousStory(story: Story): Boolean {
return super.hasPreviousStory(story)
}
}
- Create stories models (e.g. by fetching from your API and converting). For example, here is a simple story model:
val story = Story(
id = "some-UUID-or-else",
name = "story name",
isSeen = false,
previewUrl = "https://your-api.com/preview-image-url",
title = "title",
frames = listOf(
StoryFrame(
imageUrl = "https://your-api.com/frame-image-url",
content = StoryFrameContent(
controlsColor = StoryFrameControlsColor.DARK, // Color for progress and close button.
showGradients = StoryFrameShowGradients.BOTTOM, // Where to show gradient.
position = StoryFrameContentPosition.TOP, // Position of contents relative to the StoryFrame.
textColor = "#FFFFFF",
header1 = "First story frame header",
header2 = "Second story frame header",
descriptions = listOf("First line of description", "Second line of description"),
action = StoryFrameAction(
text = "Text to be displayed on clickable action button",
url = "https://your-api.com/deep-link-or-url"
),
gradientColor = "#000000"
)
)
)
)
- Add stories to
StoriesController
.
val stories = listOf(story) // sample story was created in p.2
val controller: StoriesController = StoriesCacheFactory.getInstance()
controller.clearAndAdd(StoriesConfig.All, stories)
- Start
StoriesActivity
(don't forget to add required arguments to intent).
someButton.setOnClickListener {
val intent = StoriesActivity.newIntent(
context = this,
storiesInputParams = StoriesInputParams.createDefaults()
)
startActivity(intent)
}
Setting up previews
- Add RecyclerView to your activity/fragment layout that should open
StoriesActivity
.
- In your activity/fragment (e.g.
MainActivity
), create stories previews adapter, assign it to theRecyclerView
, and implementStoriesAdapterListener
interface to open StoriesActivity ( see "Setup Stories activity" section).
class MainActivity : AppCompatActivity(), StoriesBasePreviewAdapter.StoriesAdapterListener {
private val storiesAdapter = StoriesPreviewAdapter(this@MainActivity)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById(R.id.recyclerStories).adapter = storiesAdapter
}
override fun onStoryClicked(storiesInputParams: StoriesInputParams) {
val intent = StoriesActivity.newIntent(
context = this,
storiesInputParams = storiesInputParams
)
startActivity(intent)
}
}
(note that you can create your own stories previews adapter by extending StoriesBasePreviewAdapter
).
Custom story frame view
It's possible to use a different story frame layout if you wish to change it. StoryFrameViewImpl
is used by default.
- Create your own story frame view by extending
BaseStoryFrameView
. You should set data to your views and update them inonFrameSet
.
class CustomStoryFrameView(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BaseStoryFrameView(context, attrs, defStyleAttr) {
init {
View.inflate(context, R.layout.view_custom_frame, this)
}
private val textTitle = findViewById(R.id.textTitle)
override fun onFrameSet(frame: StoryFrame) {
textTitle.text = frame.content.header1
}
}
view_custom_story.xml:
- Extend
StoryFragment
and overridecreateStoryFrameView
, where you should return yourBaseStoryFrameView
implementation. Note that you MUST pass story instance when creating your custom fragment by callingStoryFragment#addStoryToArguments
, otherwise, exception will be thrown (similar toStoriesBaseActivity
andStoriesInputParams
).
class CustomStoryFragment : StoryFragment() {
companion object {
fun newInstance(story: Story): StoryFragment =
CustomStoryFragment().addStoryToArguments(story)
}
override fun createStoryFrameView(context: Context): BaseStoryFrameView =
CustomStoryFrameView(context)
}
- Override
createStoriesFragment
in your stories activity derived fromStoriesActivity
like this:
class StoriesActivity : StoriesBaseActivity() {
override val createStoriesFragment: ((Story) -> StoryFragment) = { story ->
CustomStoryFragment.newInstance(story)
}
}
- You can change the behaviour of "is seen" status for the story by passing
StoryIsSeenWhen
toStoryFragment#addToStoryArguments
. See StoryIsSeenWhen for more details.
class StoriesActivity : StoriesBaseActivity() {
override val createStoryFragment: ((Story) -> StoryFragment) = { story ->
StoryFragment.newStoryFragmentInstance(story, StoryIsSeenWhen.TWO)
}
}
For more info, see the example .
License
The library is distributed under the MIT LICENSE. See LICENSE for details.