I would like to customize ExpandedControllerActivity in CastSDK v3 with our own UI. But right now its not permitted. Is there any way to do that ?
Currently, the customization is limited to the selection of control buttons that are present on that activity. We, however, are actively looking into expanding on that to add more capabilities; stay tuned.
If you want to customize this activity, it's gonna be a lot of work but it is possible. You can create your own layout file with the Views you want and then bind eachView to the informations present in the cast session using UIMediaController.
There is a lot of possible binding on this UIController so I suggest you try some until your find the one interesting you, here are some exemples :
val uiController = UIMediaController(this)
uiController.bindSeekBar(castSeekBar, 1000L) // Binding a CastSeekBar to video progress
uiController.bindTextViewToStreamPosition(castPositionTextView, true) // Binding a TextView to video current position
uiController.bindTextViewToStreamDuration(castDurationTextView) // Binding a TextView to video duration
Some of the informations, especially metada fields can be accesed using keys presents in MediaMetadata like title or subtitle :
uiController.bindTextViewToMetadataOfCurrentItem(castTitleTextView, MediaMetadata.KEY_TITLE)
uiController.bindTextViewToMetadataOfCurrentItem(castSubtitleTextView, MediaMetadata.KEY_SUBTITLE)
In that activity you can also directly access your video that way :
val sessionManager = CastContext.getSharedInstance(this).sessionManager
val remoteMediaClient = sessionManager.currentCastSession?.remoteMediaClient
val mediaInfo = remoteMediaClient?.mediaInfo
And here, mediaInfo will contains a lot of information like duration, metadata, contentId...
I know it's not very precise but I hope that helps :-)
Related
I am new to Epoxy and I'm currently trying some use-cases to check if it's a good fit for my project. I understand that the data that are set to a Controller should be immutable.
In my case I have a View with several toggles and checkboxes and I want to keep track of the user's interactions, because based on those interactions I need to create my network request, later on. I have searched a lot in the documentation and sample projects of Epoxy but haven't found an example with the proper way to do such a thing.
What is the correct way for the user's interaction to change the data model that my controller has.
After a lot of searching, it turns out that you should copy (shallow not deep) the list past to the controller, and change the data (interacted by the user) to the new one, and the pass it again to the controller. I just put this answer here for anyone that stumbles upon this post
val newList = ArrayList(originalList.map { it.copy() })
newList.find { it.id == event.data.id }?.isMainToggleOn = !event.data.isMainToggleOn
controller.setData(newList)
originalList = newList
Is it possible to do something like
var myThemeVariable = "redTheme" //could be either redTheme, greenTheme, or blueTheme for example
context.setTheme(R.style.myThemeVariable) //sets theme to R.style.redTheme
instead of what i usually do
var myThemeVariable = "redTheme" //could be either redTheme, greenTheme, or blueTheme
when (myThemeVariable)
{
"redTheme" -> context.setTheme(R.style.redTheme)
"greenTheme" -> context.setTheme(R.style.greenTheme)
"blueTheme" -> context.setTheme(R.style.blueTheme)
}
In this example it's not super cluttered, but in my actual code there's a lot more. My current solution is not only hard to understand, add to, or remove from; it's also (I imagine) unnecessarily computationally expensive. Is something akin to the first approach possible? If it's not, does any language have something like it? Thanks!
Thanks y'all, in the end I searched about #blackapps' and #Haseeb Hassan Asif's recommendations and found this post so I ended up doing this :)
val themeResID = context.resources.getIdentifier((sharedPreferences.getString("theme", "default_standard")), "style", context.packageName) //get theme from shared preferences
context.setTheme(themeResID)
getIdentifier uses reflection under the hood, so it has poor performance. If you're using it sparingly, like to set the theme once whenever a button is pressed, that performance difference will not be noticeable. But the code is overly complicated either way.
Just use your IDs directly like this. No Strings or when statements are necessary.
var myThemeVariable = R.style.redTheme // redTheme, greenTheme, or blueTheme
context.setTheme(myThemeVariable)
There are some controls in our app which we'd like to update the control type read out by Talkback. For example: A TextView which would better be described as a link, or an ImageView that would better be described as a button.
We could simply update the content description to report out the desired control type, though I am wondering if there is a better way? If there is another way, can it be done both through the view XML and dynamically in the code behind?
Yes, it is possible to change the type. It is called roleDescription. You would change it as follows:
ViewCompat.setAccessibilityDelegate(yourView,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(v: View, info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(v, info)
info.roleDescription = "Button"
}
})
(use string resources and translate the strings to all languages supported by your app)
This cannot be done via XML by default, but you could look into writing your own binding adapter for this.
Exo player has a widget that handles the progress called
DefaultTimeBar
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="#+id/exo_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Using this how-to programmatically seek to a particular position like 50% in android
Through inspecting the source code, it seems like DefaultTimeBarcalls update() constantly to retrieve bufferedPositionand position and draw the view accordingly. But it also has a couple of functions that call update() themselves and redraw everything. It is first initialized under the hood with a context and a few other parameters like AttributeSet.
From here, it really depends on what you're willing to do with it. If you want to just seek the position in ExoPlayer (which I assume you already know), you can just call this
val desiredPosition = simpleExoPlayer.duration / 2.toLong() // 50%
simpleExoPlayer.seekTo(desiredPosition)
However, if you're miscellaneously using the view as a Custom View and wanna update your DefaultTimeBar on your own. You need to understand how to instantiate it. Our DefaultTimeBar will receive position and bufferedPosition value updates constantly from the class that instantiated it to begin with; PlayerControlView.
Let's break it down quickly, how a DefaultTimeBar is instantiated and updated:
1- Instantiation:
val timeBar: DefaultTimeBar = DefaultTimeBar(context, null, 0, playbackAttrs)
As you can see, it takes a context, a #Nullable AttributeSet, a defStyleAttr and a #Nullable timeBar AttributeSet. Of course this can be a little bit confusing at first, but for starters, try going with the sample line of code above to instantiate it. The playbackAttrs in the constructor are the same playbackAttrs passed from SimpleExoPlayer to PlayerControlView in order to instantiate it. If playbackAttrs are still a cause of confusion, please inspect the source code of SimpleExoPlayer.
2- Updating positions: After you have instantiated your DefaultTimeBar, you can constantly call these two functions on the instance you have
timeBar.setPosition(position)
timeBar.setBufferedPosition(bufferedPosition)
3- Modifying a DefaultTimeBar view that is inflated from the XML: I have no particular experience howsoever with manipulating the DefaultTimeBar, but I assume you can just pass the generated view as a DefaultTimeBar and store it in a variable, that is:
val timeBar: DefaultTimeBar = findViewById(R.id.defaulttimebar) as DefaultTimeBar
This MAY or MAY NOT work. DefaultTimeBar extends "View" so the casting may or may not work.
If you wanna seek to a particular position, always make sure you know the target position (For example, a result of a user-induced seeking), then call setPosition to update the DefaultTimeBar.
I've created an app that has a list of cards within a RecyclerView that each have functionality of their own. I wanted to have each card choose the next color from an array defined in my colors.xml. In order to accomplish this, within my ViewHolder initialization, I set the background color of the card using cardContainer.setBackgroundColor(colors[this.layoutPosition % colors.size]. This would make it so that the colors would be cycled when more cards are created. However, I seem to be encountering the issue where my layout position is negative despite there being a set number of cards (25) created at the beginning.
While trying to search around and find the cause, I read here that if you call notifyDataSetChanged() the adapterPosition will become -1. While I am not using adapterPosition here, I thought that maybe it would be a similar issue, however, I am not adding any additional data at the time of the creation of the list items.
My ViewHolder code can be seen below. This is where the issue arises, but if any additional code is necessary feel free to ask.
class ViewHolder(itemView : View, private val listener: HabitClickListener) : RecyclerView.ViewHolder(itemView) {
val habitTitle: TextView = itemView.habitTitle
val streak: TextView = itemView.dayCounter
val cardContainer: LinearLayout = itemView.cardContainer
private val decreaseCounterButton : Button = itemView.decreaseCounterButton
private val increaseCounterButton : Button = itemView.increaseCounterButton
init {
chooseCardColor() // Choose the color for each card from the available colors
itemView.setOnClickListener {
listener.onCardClick(this.layoutPosition)
}
decreaseCounterButton.setOnClickListener {
listener.onDecrease(this.layoutPosition)
}
increaseCounterButton.setOnClickListener {
listener.onIncrease(this.layoutPosition)
}
}
private fun chooseCardColor() {
val colors = itemView.resources.getIntArray(R.array.cardColors)
cardContainer.setBackgroundColor(colors[this.layoutPosition % colors.size])
}
}
I will try to simplify this further, you should use the getAdapterPosition of ViewHolder
In recyclerview, storing the data and displaying the data are two separate things(Notice how you can use different managers(LinearLayoutManager, GridLayoutManager) to present the data in a different way.When some data changes in recyclerview, it notifies the ui to change what is shown in the screen. Even though it is really small, there is a delay between the change in the content of recyclerview and change in layout, that's why these two behave differently.
My information in this may be outdated but also don't just use the position variable as it can be inconsistent when another element is added/deleted to recyclerview due to how onBindViewHolder()(existing variables position wasn't updated when a new element is added/deleted) behaves. Instead use getAdapterPosition().
Edit: Quick fix if you don't want to deal with viewHolder gimmicks.
Add a new field to your custom object which decides what color it should be. Then make this calculation in your fragment/activity by looking at the index of your object in the list instead of doing the calculation in the viewHolder. Now you can set the color you want inside the viewHolderby looking at your object's new field. Of course you should be careful when adding/deleting a new object when you do this, but same holds true when you do it via viewHolder