im working on a new chromecast app
mCastContext?.let { nCastContext ->
if (mCastPlayer == null) {
mCastPlayer = CastPlayer(nCastContext)
}
mCastPlayer?.let { nCastPlayer ->
nCastPlayer.clearMediaItems()
val mMediaItem = when (nPlayerInput.mCastData.mFormat) {
StreamRequest.StreamFormat.DASH -> getDashMedia(nPlayerInput.mCastData)
StreamRequest.StreamFormat.HLS -> getHlsMedia(nPlayerInput.mCastData)
else -> null
}
mMediaItem?.let { nMedia ->
val mMediaQueue: ArrayList<MediaItem> = ArrayList()
mMediaQueue.add(nMedia)
nCastPlayer.setSessionAvailabilityListener(CastSessionAvailabilityListener(this))
nCastPlayer.addListener(CastEventListener(this))
nCastPlayer.playWhenReady = true
nCastPlayer.setMediaItems(ArrayList(mMediaQueue), 0, 0)
nCastPlayer.prepare()
nCastPlayer.play()
Log.d("fromCast","PLAY")
}?:run {
mInterface?.fromCastToLog("CAST_100",true)
}
}?:run {
mInterface?.fromCastToLog("CAST_101",true)
}
}
and
fun getDashMedia(nAsset: PlayerCastData): MediaItem {
return MediaItem.Builder()
.setUri(nAsset.mUrl)
.setCustomCacheKey("bala")
.setMediaMetadata(MediaMetadata.Builder().setTitle(nAsset.mTitle).setSubtitle(nAsset.mSubtitle).build())
.setMimeType(MimeTypes.APPLICATION_MPD)
.setDrmConfiguration(MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setLicenseUri(nAsset.mProxy).build())
.build()
}
How can i add stream/vod image to be displayed on CastControlActivity ? Im using androidx.media3
Im my prev old version i was able to add image and custom data using MediaMetadata. But now i can only user setTitle, setSubtitle and others but i can not see how to add image
Thx
Related
Here's the network request I have:
fun getItems(pageNumber: Int): Single<List<Item>>
Here's my lazy grid:
#Composable
fun ItemGridView(
productTiles: List<Item>,
) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier.fillMaxSize(),
) {
items(productTiles) { item -> item.toPrettyComposableView() }
}
}
Currently, my ItemGridView will stop rendering after the first page, but I would like it to continue requesting and rendering the next page after the user reaches the last item of the page. If the api response gives me an odd number of items for the first page, for the next page, it should continue filling the grid on the right side of the rendered item instead of creating a new row.
Please help
If you want pagination in your app, perhaps you might want to take a look at the AndroidX Pagination library. It handles all sorts of cases with a nice API, it also has Jetpack Compose support by importing this library implementation("androidx.paging:paging-compose:1.0.0-alpha16").
After following the official guide and trying it out in Compose you might notice that it does have support for LazyColumn and LazyRow but it does not yet have for LazyVerticalGrid.
This extension function might come in useful to you:
fun <T : Any> LazyGridScope.items(
items: LazyPagingItems<T>,
key: ((item: T) -> Any)? = null,
span: ((item: T) -> GridItemSpan)? = null,
contentType: ((item: T) -> Any)? = null,
itemContent: #Composable LazyGridItemScope.(value: T?) -> Unit
) {
items(
count = items.itemCount,
key = if (key == null) null else { index ->
val item = items.peek(index)
if (item == null) {
PagingPlaceholderKey(index)
} else {
key(item)
}
},
span = if (span == null) null else { index ->
val item = items.peek(index)
if (item == null) {
GridItemSpan(1)
} else {
span(item)
}
},
contentType = if (contentType == null) {
{ null }
} else { index ->
val item = items.peek(index)
if (item == null) {
null
} else {
contentType(item)
}
}
) { index ->
itemContent(items[index])
}
}
And you would use it like so:
// Get hold of a Flow of PagingData from your ViewModel or something similar
val pagingListFlow: Flow<PagingData<T>> = ...
val pagingList = photosPagingList.collectAsLazyPagingItems()
LazyVerticalGrid(columns = GridCells.Fixed(columnCount)) {
// Use the extension function here
items(items = pagingList) { item ->
// Draw your composable
}
}
I'm developing an Android app for recognizing augmented images and displaying videos directly over them.
I have implemented everything and it's working well but the only issue is that the anchorNode model that gets attached to the augmented image anchor isn't very stable and doesn't remain exactly in the image's frame.
I tried to follow some best practices for improving tracking and got better results but there is only one point that I'm not understanding which is "If your image will never move from its initial position (for example, a poster affixed to a wall), you can attach a global anchor to the image to increase tracking stability.", could someone help explain how to that
, please? and It would be great if I can get some tips for improving tracking evermore.
Thanks in advance.
Here is the code I'm using for tracking and creating video anchors.
override fun onSessionConfiguration(session: Session?, config: Config?) {
Log.d("ttt","onSessionConfiguration")
this.session = session
// Disable plane detection
config!!.planeFindingMode = Config.PlaneFindingMode.DISABLED
Thread().run {
database = AugmentedImageDatabase(session).also {
for(fileName in requireContext().fileList().filter { it.contains("image.jpg") }){
val file = File(requireContext().filesDir,fileName)
if(file.exists()){
it.addImage(fileName.toString().split("-")[0],BitmapFactory.decodeFile(file.absolutePath),0.25f)
}
}
}
config.setAugmentedImageDatabase(database)
}
arFragment?.setOnAugmentedImageUpdateListener { it->
run {
onAugmentedImageTrackingUpdate(it)
}
}
}
private fun onAugmentedImageTrackingUpdate(augmentedImage: AugmentedImage) {
attachVideo(augmentedImage)
}
private fun attachVideo(augmentedImage: AugmentedImage){
if (augmentedImage.trackingState == TrackingState.TRACKING
&& augmentedImage.trackingMethod == AugmentedImage.TrackingMethod.FULL_TRACKING){
if (!matrixDetected) {
matrixDetected = true
lastTracketedImageName = augmentedImage.name
anchorNode = AnchorNode(augmentedImage.createAnchor(augmentedImage.centerPose))
anchorNode!!.isSmoothed = true
texture = ExternalTexture().also {
it.surfaceTexture.setOnFrameAvailableListener {
texture!!.surfaceTexture.setOnFrameAvailableListener(null)
val model = ShapeFactory.makeCube(
Vector3(augmentedImage.extentX,0f,augmentedImage.extentZ),
Vector3(0f,0f,0f),
plainVideoMaterial
)
model.isShadowReceiver = false
model.isShadowCaster = false
val renderable = anchorNode!!.setRenderable(model)
renderable.material!!.setExternalTexture("videoTexture",texture)
fadeInVideo(renderable.material)
}
}
val videoFile = File(requireContext().filesDir,"${augmentedImage.name}-video.mp4")
mediaPlayer = MediaPlayer.create(requireContext(),Uri.fromFile(videoFile)).also {
it.isLooping = true
it.setSurface(texture!!.surface)
it.start()
}
arFragment!!.arSceneView.scene.addChild(anchorNode)
}
}else{
if(matrixDetected){
if(anchorNode != null){
arFragment!!.arSceneView.scene.removeChild(anchorNode!!)
anchorNode = null
}
viewLifecycleOwner.lifecycleScope.launch {
val result = viewModel.endArVideo(deviceID,augmentedImage.name)
if(result.isSuccessful){
Log.d("ttt","stop ar request success")
}else{
Log.d("ttt","stop ar request failed ${result.errorBody()}")
}
}
matrixDetected = false
}
mediaPlayer?.let {
it.setSurface(null)
it.stop()
it.release()
mediaPlayer = null
}
}
}
private fun fadeInVideo(material: Material) {
ValueAnimator.ofFloat(0f, 1f).apply {
duration = 500L
interpolator = LinearInterpolator()
addUpdateListener { v ->
material.setFloat("videoAlpha", v.animatedValue as Float)
}
}.start()
}
In the following code, I have two parts A and B. I need to extract part B as a common part for more of my pages.
But it contains item, I cannot extract item, but item must be included in the if judgment because paging3 will scroll to the top for extra item.
Is there a way to extract item?
LazyColumn(Modifier.fillMaxSize()) {
// Part A
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
// Part B
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
navController.navigate("blowWind")
}
}
}
} else {
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
I solved this problem
fun <T : Any> LazyListScope.newItems(pagingItems: LazyPagingItems<T>) {
val refresh = pagingItems.loadState.refresh
val append = pagingItems.loadState.append
if (refresh is LoadState.NotLoading && append is LoadState.NotLoading) {
if (pagingItems.itemCount == 0) {
item {
PosterCompose() {
}
}
}
}else{
item {
LoadStateView(path = FOLLOW_WIND_LIST, refresh = refresh, append = append) {
pagingItems.retry()
}
}
}
}
This is how to use
LazyColumn(Modifier.fillMaxSize()) {
items(pagingItems) { wind ->
WindRow(navController, wind!!)
}
newItems(pagingItems)
}
i am working on QuizApp and I get a problem when I choose an answer and click submit it show me the correct and wrong answer and then I should click submit again to go to the next question, i want to disable that , I want when someone click submit he cant change the answer or choose another answer
here is my code
override fun onClick(v: View?) {
when (v?.id) {
R.id.tv_option_one -> {
selectedOptionView(tv_option_one, 1)
}
R.id.tv_option_two -> {
selectedOptionView(tv_option_two, 2)
}
R.id.tv_option_three -> {
selectedOptionView(tv_option_three, 3)
}
R.id.tv_option_four -> {
selectedOptionView(tv_option_four, 4)
}
R.id.btn_submit -> {
if (mSelectedOptionPosition == 0) {
mCurrentPosition++
when {
mCurrentPosition <= mQuestionsList!!.size -> {
setQuestion()
}
else -> {
// TODO (STEP 5: Now remove the toast message and launch the result screen which we have created and also pass the user name and score details to it.)
// START
val intent =
Intent(this, ResultActivity::class.java)
intent.putExtra(Constants.USER_NAME, mUserName)
intent.putExtra(Constants.CORRECT_ANSWERS, mCorrectAnswers)
intent.putExtra(Constants.TOTAL_QUESTIONS, mQuestionsList!!.size)
startActivity(intent)
finish()
// END
}
}
} else {
val question = mQuestionsList?.get(mCurrentPosition - 1)
// This is to check if the answer is wrong
if (question!!.correctAnswer != mSelectedOptionPosition) {
answerView(mSelectedOptionPosition, R.drawable.wrong_option_border_bg)
//
}
else {
mCorrectAnswers++
}
// This is for correct answer
answerView(question.correctAnswer, R.drawable.correct_option_border_bg)
if (mCurrentPosition == mQuestionsList!!.size) {
btn_submit.text = "FINISH"
} else {
btn_submit.text = "GO TO NEXT QUESTION"
}
mSelectedOptionPosition = 0
}
}
}
}
private fun setQuestion() {
val question = mQuestionsList!!.get(mCurrentPosition - 1) // Getting the question from the list with the help of current position.
defaultOptionsView()
if (mCurrentPosition == mQuestionsList!!.size) {
btn_submit.text = "FINISH"
} else {
btn_submit.text = "SUBMIT"
}
progressBar.progress = mCurrentPosition
tv_progress.text = "$mCurrentPosition" + "/" + progressBar.getMax()
tv_question.text = question.question
iv_image.setImageResource(question.image)
tv_option_one.text = question.optionOne
tv_option_two.text = question.optionTwo
tv_option_three.text = question.optionThree
tv_option_four.text = question.optionFour
}
/**
* A function to set the view of selected option view.
*/
private fun selectedOptionView(tv: TextView, selectedOptionNum: Int) {
defaultOptionsView()
mSelectedOptionPosition = selectedOptionNum
tv.setTextColor(
Color.parseColor("#363A43")
)
tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.background = ContextCompat.getDrawable(
this,
R.drawable.selected_option_border_bg
)
}
/**
* A function to set default options view when the new question is loaded or when the answer is reselected.
*/
private fun defaultOptionsView() {
val options = ArrayList<TextView>()
options.add(0, tv_option_one)
options.add(1, tv_option_two)
options.add(2, tv_option_three)
options.add(3, tv_option_four)
for (option in options) {
option.setTextColor(Color.parseColor("#7A8089"))
option.typeface = Typeface.DEFAULT
option.background = ContextCompat.getDrawable(
this,
R.drawable.default_option_border_bg
)
}
}
/**
* A function for answer view which is used to highlight the answer is wrong or right.
*/
private fun answerView(answer: Int, drawableView: Int) {
when (answer) {
1 -> {
tv_option_one.background = ContextCompat.getDrawable(
this,
drawableView
)
}
2 -> {
tv_option_two.background = ContextCompat.getDrawable(
this,
drawableView
)
}
3 -> {
tv_option_three.background = ContextCompat.getDrawable(
this,
drawableView
)
}
4 -> {
tv_option_four.background = ContextCompat.getDrawable(
this,
drawableView
)
}
}
}
}
in your onclick, change the other views
view.isClickable = false
I'm using mapbox, with GeoJsonSource and symbollayer. When user clicks on feature it should change a color. I handle this logic with following code and it works, but it is too slow and takes several second to change icon color.
Here I configure symbol layer, add icon changelogic for 'PROPERTY_SELECTED':
mapBoxMap?.addLayer(SymbolLayer(markerStyleLayerIdentifier, markerSourceIdentifier)
.withProperties(
PropertyFactory.iconImage(markerImage),
PropertyFactory.iconAllowOverlap(false),
PropertyFactory.iconImage(match(
get(PROPERTY_SELECTED), literal(0),
literal(markerImage),
literal(markerImageSelected)
))
))
on map click features objects are update:
override fun onMapClick(point: LatLng) {
val screenPoint = mapBoxMap?.projection?.toScreenLocation(point)
var features = mapBoxMap?.queryRenderedFeatures(screenPoint
?: return, markerStyleLayerIdentifier)
if ((features ?: return).isNotEmpty()) {
var feature = features[0]
showMarkerInfo(feature)
doAsync {
var featureList = featureCollection?.features()
var id = feature.getNumberProperty(PROPERTY_STOP_ID)
if (featureList != null) {
for (i in 0 until featureList.size) {
var fId = featureList[i].getNumberProperty(PROPERTY_STOP_ID)
if (fId == id) {
featureList[i].properties()?.addProperty(PROPERTY_SELECTED, 1)
} else {
featureList[i].properties()?.addProperty(PROPERTY_SELECTED, 0)
}
}
uiThread {
refreshSource()
}
}
}
}
}
and refresh source :
private fun refreshSource() {
var source = mapBoxMap?.getSource(markerSourceIdentifier) as GeoJsonSource?
if (source != null && featureCollection != null) {
source.setGeoJson(featureCollection)
}
}
after 'refreshSource' is called , it takes several time before icon update. In my case there are 2050 features is source. Is there any better way to implement it ? Or any way to optimise this solution ?
here is a second , faster way from github answer:
var selectedLayer = mapBoxMap?.getLayer(markerSelectedStyleLayerIdentifier) as SymbolLayer?
var id = feature.getNumberProperty(PROPERTY_STOP_ID)
var selectedExpression = any(
eq(get(PROPERTY_STOP_ID), literal(id.toString()))
)
selectedLayer?.filter = selectedExpression
you can see whole issue there
https://github.com/mapbox/mapbox-java/issues/892