I am using Exoplayer to play some videos. I have some .mpd live url's which comes from a backend. Normally if the broadcast is live, the seek bar is set to right end side and on the left end side live video cache time is stated with minus value like (-00.59.51). I want to achive that in my project. But by default, current position of the video stated on left side and video duration on right side.
There is two problem:
1.We have to detect if the video is live or not.
2.We need to set time values for live video.
private fun initializePlayerXml(currentXmlLink: String) {
if (player == null) {
val trackSelector = DefaultTrackSelector(this)
trackSelector.setParameters(
trackSelector.buildUponParameters().setMaxVideoSizeSd()
)
try {
player = SimpleExoPlayer.Builder(this)
.build()
} catch (e: Exception) {
}
}
if (player!!.bufferedPosition == 0L) {
playButton.setImageResource(R.drawable.ic_pause)
}
playButton.setOnClickListener {
if (player!!.isPlaying) {
player!!.pause()
playButton.setImageResource(R.drawable.ic_play)
} else {
player!!.play()
playButton.setImageResource(R.drawable.ic_pause)
}
}
val doubleClickForwardFun = DoubleClick(object : DoubleClickListener {
override fun onSingleClickEvent(view: View?) {
}
override fun onDoubleClickEvent(view: View?) {
val currentPosition = player!!.currentPosition
player!!.seekTo(currentPosition + 10000)
}
})
val doubleClickBackwardFun = DoubleClick(object : DoubleClickListener {
override fun onSingleClickEvent(view: View?) {
}
override fun onDoubleClickEvent(view: View?) {
val currentPosition = player!!.currentPosition
player!!.seekTo(currentPosition - 10000)
}
})
doubleClickBackward.setOnClickListener(doubleClickBackwardFun)
doubleClickForward.setOnClickListener(doubleClickForwardFun)
forwardButton.setOnClickListener {
val currentPosition = player!!.currentPosition
player!!.seekTo(currentPosition + 10000)
}
backwardButton.setOnClickListener {
val currentPosition = player!!.currentPosition
player!!.seekTo(currentPosition - 10000)
}
val mediaItem =
MediaItem.Builder()
.setUri(currentXmlLink)
.setMimeType(MimeTypes.APPLICATION_MPD)
.build()
player!!.addMediaItem(mediaItem)
playerView!!.player = player
player!!.playWhenReady = playWhenReady
player!!.seekTo(currentWindow, playbackPosition)
playbackStateListener.let { player!!.addListener(it) }
player!!.prepare()
}
I have already a question on Exoplayer/Issues, you can check here. Also my repo is here.
Related
Im using Google Play Core library to implement a rating in app, when a user select a button a rate in app is displayed and the user can rate the app with a comment and with stars. My questions is, How can I get the number of stars (rate) given by an user?
override fun showRateAppOnPlayStoreDialog() {
startReviewInfo()
}
private fun startReviewInfo(){
if(reviewInfo!=null){
val flow = manager.launchReviewFlow(this, reviewInfo)
flow.addOnCompleteListener {
it.result
}
}else{
}
}
private fun activateReviewInfo(){
manager = ReviewManagerFactory.create(this)
val request = manager.requestReviewFlow()
request.addOnCompleteListener { task ->
if (task.isSuccessful) {
//We got the ReviewInfo object
reviewInfo = task.result
} else {
}
}
}
I want to fetch data from the server using Retrofit but it shows me HTTP 500 server error I know it is due to a null value in parameters but I don't where is the null value comes from. I try my best to find the null value but can't find it. If any other reason then please tell me.
Here is my Fragment Code
#RequiresApi(Build.VERSION_CODES.M)
override fun inOnCreateView(mRootView: ViewGroup, savedInstanceState: Bundle?) {
val homeActivity = activity as HomeNavHostActivity
homeActivity.toolbar_id?.visibility = View.VISIBLE
homeActivity.toolbar_search_icon_id.visibility = View.VISIBLE
homeActivity.toolbar_add_icon_id.visibility = View.GONE
homeActivity.home_view_layout?.visibility = View.VISIBLE
homeActivity.bottom_layout?.visibility = View.VISIBLE
homeActivity.toolbar_title_tv.text = "Home"
homeActivity.toolbar_search_icon_id.setOnClickListener() {
showSearchDialog(mRootView)
}
homeActivity.cancel_text.setOnClickListener() {
homeActivity.search_layout.visibility = View.GONE
homeActivity.toolbar_title_tv.visibility = View.VISIBLE
homeActivity.search_view?.setQuery("", false)
homeActivity.search_view?.clearFocus()
}
val dialogHelper by inject<MaterialDialogHelper>()
setupProgressDialog(viewModel.showHideProgressDialog, dialogHelper)
if (isNetworkAvailable(requireContext())) {
var area:String = "20"
var zipcode:String = "WC2N5DU"
viewModel.getSkipFilterList(zipcode, area)
} else {
showAlertDialog(getString(R.string.no_internet))
}
attachViewModel()
}
Here is my ViewModel Code
var filterSkipList: MutableLiveData<SkipListResponse> = MutableLiveData()
fun getSkipFilterList(zipcode: String, area: String) {
viewModelScope.launch {
_showHideProgressDialog.value = true.wrapWithEvent()
sharedWebServices.getFilterSkip(zipcode, area).run {
onSuccess {
_showHideProgressDialog.value = false.wrapWithEvent()
if (it.code == VALID_STATUS_CODE) {
filterSkipList.value = it
}else {
showSnackbarMessage(it.message)
}
}
onFailure {
_showHideProgressDialog.value = false.wrapWithEvent()
it.message?.let { it1 -> showSnackbarMessage(it1) }
}
}
}
}
Here is my data class
#Serializable
data class SkipFilterList(
val zipcode:String,
val area:String
)
Here is my Post
#POST("search-skip")
suspend fun skipListing(
#Header("Authorization") token: String?,
#Body body: SkipFilterList): SkipListResponse
Here is My Repostry
suspend fun getFilterSkip(
zipcode: String,
area: String
) = withContext(dispatcher) {
val token = SharePrefrenceHelper.getInstance(app).getToken()
val body = SkipFilterList(zipcode, area)
safeApiCall {
Result.success(apiServices.skipListing("Bearer" + token, body))
}
}
By passing json object in the body this was solved.
val jsonObject = JsonObject()
jsonObject.addProperty("zipcode", zipcode)
jsonObject.addProperty("radius", area)
i am working on an online shopping application using retrofit, coroutine, livedata, mvvm,...
i want to show progress bar before fetching data from server for afew seconds
if i have one api request i can show that but in this app i have multiple request
what should i do in this situation how i should show progress bar??
Api Service
#GET("homeslider.php")
suspend fun getSliderImages(): Response<List<Model.Slider>>
#GET("amazingoffer.php")
suspend fun getAmazingProduct(): Response<List<Model.AmazingProduct>>
#GET("handsImages.php")
suspend fun getHandsFreeData(
#Query(
"handsfree_id"
) handsfree_id: Int
): Response<List<Model.HandsFreeImages>>
#GET("handsfreemoreinfo.php")
suspend fun gethandsfreemoreinfo(): Response<List<Model.HandsFreeMore>>
#GET("wristmetadata.php")
suspend fun getWristWatchMetaData(
#Query(
"wrist_id"
) wrist_id: Int
): Response<List<Model.WristWatch>>
repository
fun getSliderImages(): LiveData<List<Model.Slider>> {
val data = MutableLiveData<List<Model.Slider>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getSliderImages()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getAmazingOffer(): LiveData<List<Model.AmazingProduct>> {
val data = MutableLiveData<List<Model.AmazingProduct>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getAmazingProduct()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getHandsFreeData(handsree_id: Int): LiveData<List<Model.HandsFreeImages>> {
val dfData = MutableLiveData<List<Model.HandsFreeImages>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getHandsFreeData(handsree_id)
withContext(Main + SupervisorJob(job)) {
dfData.value = response.body()
}
job.complete()
job.cancel()
}
return dfData
}
fun getHandsFreeMore(): LiveData<List<Model.HandsFreeMore>> {
val data = MutableLiveData<List<Model.HandsFreeMore>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.gethandsfreemoreinfo()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
VIEWMODEL
fun getSliderImages() = repository.getSliderImages()
fun getAmazingOffer() = repository.getAmazingOffer()
fun recieveAdvertise() = repository.recieveAdvertise()
fun dailyShoes(context: Context) = repository.getDailyShoes(context)
i will appreciate your help
I couldn't help but notice that your repository contains lots of repetitive code. first point to learn here is that all that logic in Repository, it usually goes in the ViewModel. second thing is that you are using applicationScope to launch your coroutines, which usually is done using viewModelScope(takes care of cancellation) object which is available in every viewModel.
So first we have to take care of that repetitive code and move it to ViewModel. So your viewModel would now look like
class YourViewModel: ViewModel() {
// Your other init code, repo creation etc
// Live data objects for progressBar and error, we will observe these in Fragment/Activity
val showProgress: MutableLiveData<Boolean> = MutableLiveData()
val errorMessage: MutableLiveData<String> = MutableLiveData()
/**
* A Generic api caller, which updates the given live data object with the api result
* and internally takes care of progress bar visibility. */
private fun <T> callApiAndPost(liveData: MutableLiveData<T>,
apiCall: () -> Response<T> ) = viewModelScope.launch {
try{
showProgress.postValue(true) // Show prgress bar when api call is active
if(result.code() == 200) { liveData.postValue(result.body()) }
else{ errorMessage.postValue("Network call failed, try again") }
showProgress.postValue(false)
}
catch (e: Exception){
errorMessage.postValue("Network call failed, try again")
showProgress.postValue(false)
}
}
/******** Now all your API call methods should be called as *************/
// First declare the live data object which will contain the api result
val sliderData: MutableLiveData<List<Model.Slider>> = MutableLiveData()
// Now call the API as
fun getSliderImages() = callApiAndPost(sliderData) {
repository.getSliderImages()
}
}
After that remove all the logic from Repository and make it simply call the network methods as
suspend fun getSliderImages() = api.getSliderImages() // simply delegate to network layer
And finally to display the progress bar, simply observe the showProgress LiveData object in your Activity/Fragment as
viewModel.showProgress.observer(this, Observer{
progressBar.visibility = if(it) View.VISIBLE else View.GONE
}
First create a enum class status:
enum class Status {
SUCCESS,
ERROR,
LOADING
}
Then create resource class like this:
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
Now add your request to a list of response:
var list = java.util.ArrayList<Response<*>>()
suspend fun getApis() = list.addAll(
listOf(
api.advertise(),
api.getAmazingProduct(),
api.dailyShoes(),
api.getSliderImages(),
.
.
.
)
)
In your viewmodel class:
private val _apis = MutableLiveData<Resource<*>>()
val apis: LiveData<Resource<*>>
get() = _apis
init {
getAllApi()
}
fun getAllApi() {
val job = Job()
viewModelScope.launch(IO + job) {
_apis.postValue(
Resource.loading(null)
)
delay(2000)
repository.getApis().let {
withContext(Main + SupervisorJob(job)) {
it.let {
if (it) {
_apis.postValue(Resource.success(it))
} else {
_apis.postValue(Resource.error("Unknown error eccured", null))
}
}
}
}
job.complete()
job.cancel()
}
}
Now you can use status to show progress like this . use this part in your target fragment:
private fun setProgress() {
viewModel.apis.observe(viewLifecycleOwner) {
when (it.status) {
Status.SUCCESS -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.VISIBLE
parentscroll.visibility = View.VISIBLE
}
}
Status.ERROR -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
Status.LOADING -> {
binding.apply {
progress.visibility = View.VISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
}
}
}
I hope you find it useful.
I am trying to access the googleFit API.
It seems pretty straightforward. Get the google sign-in permissions and required authorizations then query for Step count.
My code doesn't seem to work.
When I debug this the fitnessOption declaration part throws "source code doesn't match byte code" error. I cleaned my project, rebuild it it didn't work
Android gurus, Where am I going wrong??
fun getAuthorizationAndReadData() {
try {
MainActivity().fitSignIn(FitActionRequestCode.READ_DATA)
} catch () {
Log.i("e", "error!!!!")
}
}
MainActivity
enum class FitActionRequestCode {
READ_DATA
}
private val fitnessOptions: GoogleSignInOptionsExtension = FitnessOptions.builder()
.addDataType(DataType.TYPE_STEP_COUNT_DELTA, FitnessOptions.ACCESS_READ).build()
fun fitSignIn(requestCode: FitActionRequestCode) {
if (oAuthPermissionsApproved()) {
readHistoryData()
} else {
requestCode.let {
GoogleSignIn.requestPermissions(
this,
requestCode.ordinal,
getGoogleAccount(), fitnessOptions)
}
}
}
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
private fun performActionForRequestCode(requestCode: FitActionRequestCode) = when (requestCode) {
FitActionRequestCode.READ_DATA -> readHistoryData()
}
private fun readHistoryData(): Task<DataReadResponse> {
// Begin by creating the query.
val readRequest = queryFitnessData()
// Invoke the History API to fetch the data with the query
return Fitness.getHistoryClient(this, getGoogleAccount())
.readData(readRequest)
.addOnSuccessListener { dataReadResponse ->
printData(dataReadResponse)
Log.i(ContentValues.TAG, "Data read was successful!") }
.addOnFailureListener { e ->
Log.e(ContentValues.TAG, "There was a problem reading the data.", e)
}
}
private fun queryFitnessData(): DataReadRequest {
// [START build_read_data_request]
// Setting a start and end date using a range of 1 week before this moment.
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
val now = Date()
calendar.time = now
val endTime = calendar.timeInMillis
calendar.add(Calendar.WEEK_OF_YEAR, -1)
val startTime = calendar.timeInMillis
return DataReadRequest.Builder()
.aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
.bucketByTime(1, TimeUnit.DAYS)
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build()
}
Try following steps once.
Click Build -> Clean then Disable Instant Run, in Settings -> Build, Execution, Deployment
I have MapBox APi. but in the last update API deleted markers from MapBox class. Haw can I select my added symbols and start events?
override fun onMapReady(mapboxMap: MapboxMap) {
this.mapBoxMap = mapboxMap
val query = Query()
query.getAllParking { parking ->
allParking = parking
this.mapBoxMap.setStyle(
Style.DARK
) { map ->
// добавляем метку на карту
map.addImage(
"marker-icon-id",
BitmapFactory.decodeResource(
this.resources,
R.drawable.mapbox_marker_icon_default
)
)
parking.forEach {
try {
val geoJsonSource = GeoJsonSource(
it.Parking_id,
Feature.fromGeometry(
Point.fromLngLat(
it.Parking_longitude.toDouble(),
it.Parking_latitude.toDouble()
)
)
)
map.addSource(geoJsonSource)
} catch (ex: NumberFormatException) {
print(ex.localizedMessage)
}
val symbolLayer = SymbolLayer(it.Parking_id, it.Parking_id)
symbolLayer.withProperties(PropertyFactory.iconImage("marker-icon-id"))
map.addLayer(symbolLayer)
}
enableLocationComponent(map)
}
}
}
}
I expect the output the symbolClickListener()