runtime error while accessing google api in android emulator - java

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

Related

I am trying to make a smart contract transaction. My transactions never gets mined

I have a smart contract deployed. Whenever I try to perform a transaction, the transaction starts, but that's it. The transaction never actually takes place (or "doesn't get mined" might be the right word).
For reference, my smart contract function has takes one parameter and gives no output. just stores it. It's not that complex.
I do have reason to believe this has something to do with the transaction manager that I have set and how it doesn't provide enough nonce for any miner to take into consideration, since that was one of the answers on another question. However, they never said what the method was to fix it. I tried my best to fix it using a static gasProvider in hopes that it might be enough.
I have two pieces of code below:
VoteContractDelegate - This is used by rest of the application to make requests.
VoteContractAccessor - This extends the contract class and is used to return remotecall requests.
Note: I am using the web3j library.
VoteContractDelegate
private const val TAG = "VoteContractDelegate"
class VoteContractDelegate() {
//-----------------------------------------------------------------------------contract-elements
// TODO: get this from data store
private val USER_PRIVATE_KEY =
"66c53799ee0c63f2564305e738ea7479d7aee84aed3aac4c01e54a7acbcc4d92"
private val ROPSTEN_INFURA_URL = "https://ropsten.infura.io/v3/c358089e1aaa4746aa50e61d4ec41c5c"
private val credentials = Credentials.create(USER_PRIVATE_KEY)
private lateinit var web3j: Web3j//--------------------------------------------works-as-intended
private lateinit var contract: VoteContractAccessor
// TODO: set correct value
private val gasPrice: BigInteger = Convert.toWei("40", Convert.Unit.GWEI).toBigInteger()
//this gas limit value is from deployment and has to be constant
private val gasLimit = BigInteger.valueOf(4000000)
private val gasProvider: ContractGasProvider = StaticGasProvider(gasPrice, gasLimit)
init {
instantiateWeb3J()
web3j.ethGetTransactionCount(credentials.address, DefaultBlockParameterName.LATEST)
initializeContract()
}
private fun instantiateWeb3J() {
Log.d(
TAG, try {
web3j = Web3j.build(HttpService(ROPSTEN_INFURA_URL))
"Connection Successful"
} catch (e: Exception) {
e.printStackTrace()
"Connection Unsuccessful, error : ${e.message}"
}
)
}
private fun initializeContract() {
Log.d(
TAG, try {
contract = VoteContractAccessor(
web3j,
RawTransactionManager(web3j, credentials, ChainIdLong.ROPSTEN),
newGasProvider, credentials
)
"Contract Initialized Successfully"
} catch (e: Exception) {
"Contract Initialization Error"
}
)
}
// TODO: this is the problem function...
fun casteVote(party: PartyEnum): String {
return try {
Log.d(TAG,"started casteVote")
/**
* this starts but never completes. No errors outputted.
* If you watch closely, You'll see a '9' inside the get.
* that is the timeout duration. This function times out
* before being executed. It keeps waiting for whole 9
* minutes and then outputs a timeout error
*/
"cost = " + (
contract
.putVote(party)
.sendAsync()
.get(9, TimeUnit.MINUTES)
.gasUsed
.toString()
)
/**
* returns null as a result.
*/
} catch (e: Exception) {
e.printStackTrace()
e.message.toString()
}
}
}
VoteContractAccessor:
private const val TAG = "VoteContractAccessor"
private const val CONTRACT_BINARY =
"608060405234801561001057600080fd5b506000600281905560038190556004556103be8061002f6000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063449b6db21161005b578063449b6db2146100b8578063464d4a92146100db578063a483f4e1146100ee578063f5a31579146100f657600080fd5b8063068ea0eb146100825780630efcb43c1461008c57806342cff738146100a3575b600080fd5b61008a6100fe565b005b6004545b6040519081526020015b60405180910390f35b6100ab6101cb565b60405161009a9190610312565b3360009081526020819052604090205460ff16604051901515815260200161009a565b61008a6100e93660046102f9565b61022d565b600354610090565b600254610090565b3360009081526020819052604090205460ff16156101635760405162461bcd60e51b815260206004820152601b60248201527f596f75204861766520416c7265616479204265656e204164646564000000000060448201526064015b60405180910390fd5b336000818152602081905260408120805460ff191660019081179091558054808201825591527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805473ffffffffffffffffffffffffffffffffffffffff19169091179055565b6060600180548060200260200160405190810160405280929190818152602001828054801561022357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610205575b5050505050905090565b60048110801561023d5750600081115b6102af5760405162461bcd60e51b815260206004820152603960248201527f74686520676976656e206e756d62657220697320696e76616c6964206173207460448201527f6865206e756d626572206973206f7574206f662072616e676500000000000000606482015260840161015a565b80600114156102d057600280549060006102c88361035f565b919050555050565b80600214156102e957600380549060006102c88361035f565b600480549060006102c88361035f565b60006020828403121561030b57600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156103535783516001600160a01b03168352928401929184019160010161032e565b50909695505050505050565b600060001982141561038157634e487b7160e01b600052601160045260246000fd5b506001019056fea264697066735822122036c7a6eb905bfb9bbcbf2436975878a32327f0fd4a8648f67281fd07be0ff4b064736f6c63430008070033"
private const val CONTRACT_ADDRESS = "0x84D46ba7aAac6221DF9038d3Ccf41F1cd46001aF"
class VoteContractAccessor(
private val web3obj: Web3j,
transactionManager: TransactionManager,
gasProvider: ContractGasProvider,
private val credentials: Credentials
) :
Contract(
CONTRACT_BINARY,
CONTRACT_ADDRESS,
web3obj,
transactionManager,
gasProvider
) {
//--------------------------------------------------------------------------------function-names
private val functionGetHasAlreadyVoted = "hasAlreadyVoted"
private val functionRegisterVote = "registerVote"
private val functionGetAddressValues = "getAddressValues"
private val functionGetPartyOneVotes = "getParty1Votes"
private val functionGetPartyTwoVotes = "getParty2Votes"
private val functionGetPartyThreeVotes = "getParty3Votes"
fun printBalance(): BigDecimal {
val balance =
Convert.fromWei(
web3obj.ethGetBalance(
credentials.address, DefaultBlockParameterName.LATEST
)
.send().balance.toString(),
Convert.Unit.ETHER
)
Log.d(TAG, "user balance = $balance")
return balance
}
fun putVote(party: PartyEnum): RemoteCall<TransactionReceipt> {
Log.d(TAG, "putVote\n\n\nstarted")
val function = Function(
functionRegisterVote,
listOf<Type<*>>(
Uint256(
when (party) {
PartyEnum.ONE -> 1
PartyEnum.TWO -> 2
PartyEnum.THREE -> 3
}
)
),
emptyList()
)
Log.d(TAG, "gas price = ${gasProvider.getGasPrice(functionRegisterVote)}")
return executeRemoteCallTransaction(function)
}
}
PartyEnum
enum class PartyEnum {
ONE, TWO, THREE
}
You can use the credentials provided if you want. It doesn't have any real ether anyway.
Instructions for if you want to look at the entire project...
if you want the entire project links, Github links -
Android studio project
Solidity project link
Once you open and run the android studio project, swipe right on the left side of the screen to open the drawer layout side menu (the navigation button for drawer layout doesn't work yet).
From there, open the contract interface option and then, you'll see the select party card on the top.
That is the function responsible for the voting transactions.
The back end for this card will be found in the directory named ui by the name ContractScreenFragment.
Then, somewhere at the top of that file you can find the onclick listener for the select party to vote card.
Here's what it would look like-
casteVoteButton.setOnClickListener {
viewModel.transactionCost.value = transactionInProgress
CoroutineScope(Dispatchers.IO).launch {
val cost = voteContractDelegate.casteVote(partyEnum)
Log.d(TAG, "transaction output:-\n$cost")
CoroutineScope(Dispatchers.Main).launch {
viewModel.transactionCost.value = cost
}
}
}
the rest of the functions have been mentioned above.
For a less complicated code, try checking the test button onclick listener at the end of the same file. That should be easier to understand.
That onclick listener is connected to the bottom card found inside the contract interface. you'll enter the gas price in Gwei in the edit text and then call the same function.

show progress bar before fetching data from server using retrofit

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.

Exoplayer How to set duration/position manually for live streams?

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.

My Service won't stop even if is onDestroy fired

I have a Service that acts like a TimerTask and even if I stop the service using :
val intent = Intent(applicationContext, TimeService::class.java)
stopService(intent)
It doesn't stop, I have a Log in onDestroy and this Log is fired... but I think the problem is that I'm using a TimerTask inside the Service this is my Service
class TimeService : Service() {
private val mHandler = Handler()
var calendar: Calendar? = null
var simpleDateFormat: SimpleDateFormat? = null
var strDate: String? = null
var date_current: Date? = null
var date_diff: Date? = null
private var mTimer: Timer? = null
private val NOTIFY_INTERVAL: Long = 1000
var intent: Intent? = null
companion object {
val str_receiver = "myreceiver"
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
calendar = Calendar.getInstance()
simpleDateFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
mTimer = Timer()
mTimer!!.scheduleAtFixedRate(TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL)
intent = Intent(str_receiver)
}
internal inner class TimeDisplayTimerTask : TimerTask() {
override fun run() {
mHandler.post {
calendar = Calendar.getInstance()
simpleDateFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
strDate = simpleDateFormat!!.format(calendar!!.time)
Log.e("strDate", strDate)
twoDatesBetweenTime()
}
}
}
fun twoDatesBetweenTime(): String {
try {
date_current = simpleDateFormat!!.parse(strDate)
} catch (e: Exception) {
}
try {
date_diff = simpleDateFormat!!.parse(SharedPreferenceHelper.defaultPrefs(this).getString("data", ""))
} catch (e: Exception) {
}
try {
val diff = date_current!!.time - date_diff!!.time
val timeInSeconds = Integer.valueOf(SharedPreferenceHelper.defaultPrefs(this).getString("seconds", "")!!)
val timeTimer = TimeUnit.SECONDS.toMillis(timeInSeconds.toLong())
val diffWithTime = timeTimer - diff
val diffSeconds2 = diffWithTime / 1000 % 60
val diffMinutes2 = diffWithTime / (60 * 1000) % 60
val diffHours2 = diffWithTime / (60 * 60 * 1000) % 24
if (diffWithTime >= 0) {
val counterTime = "$diffHours2 : $diffMinutes2 : $diffSeconds2"
Log.e("TIME", counterTime)
fn_update(counterTime)
} else {
SharedPreferenceHelper.defaultPrefs(this).edit().putBoolean("finish", true).apply()
mTimer!!.cancel()
}
} catch (e: Exception) {
mTimer!!.cancel()
mTimer!!.purge()
}
return ""
}
override fun onDestroy() {
super.onDestroy()
Log.e("Service finish", "Finish")
}
private fun fn_update(str_time: String) {
intent!!.putExtra("time", str_time)
sendBroadcast(intent)
}
}
And the problem is that this log :
Log.e("strDate", strDate)
And this log :
Log.e("TIME", counterTime)
Never stops, what I'm missing?
EDIT
My approach is this from the moment but I don't know if it's the best way :
override fun onDestroy() {
super.onDestroy()
Log.e("Service finish", "Finish")
if(mTimer!=null){
mTimer!!.cancel()
mTimer!!.purge()
}
}
OnDestroy is a callback method invoked by system to allow your service a "clean exit":
Called by the system to notify a Service that it is no longer used and is being removed. The service should clean up any resources it holds (threads, registered receivers, etc) at this point. Upon return, there will be no more calls in to this Service object and it is effectively dead. Do not call this method directly.
System does not instantly terminate your app process following this callback. It's up to You to kill the TimerTask at this point. If You leave it running it's considered a leak. Most likely it will keep running until system decides it's time to kill Your apps process which might take a while if it's kept in the foreground.

Android video trimming library

I am doing Video Triming and using k4l-video-trimmer library. I am getting an issue. I have downloaded the latest code and integrate it on Android Studio. When i select a video, k4l-video-trimmer successfully prepared the video and correctly shows video info and snapshots. I have set the max duration to 10 sec but when move the progressbar to crop the video at specific duration, the cropping duration which is showing on screen like (01:21 sec - 01:31 sec) for 10 sec will change to (01:21 sec - 01:36 sec) becomes 15 sec duration that is an issue and when I crop the video, it will crop it for 23 sec. I don't know how to resolve this issue. Please help me to resolve this issue
You have to implement MediaRecorder.OnInfoListener to manually stop the recording at 10 seconds. Once its stopped, the MediaRecorder goes back to the initial state and the setup has to be done again to start back recording.
public class VideoCapture extends Activity implements MediaRecorder.OnInfoListener {
public void startVideoRecording() {
// Normal MediaRecorder Setup
recorder.setMaxDuration(10000); // 10 seconds
recorder.setOnInfoListener(this); // very important
}
public void onInfo(MediaRecorder mrc, int mri, int extra) {
if (mri == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
Log.v("VIDEOCAPTURE","10 seconds");
mrc.stop();
}
}
}
Now, For the Progress bar you can use a Timer.
//fires once a second, decrease this to fire more frequently
private static final int TIMER_FREQ = 1000;
final ProgressBar progressBar = new ProgressBar(this); //where this is a Context
progressBar.setMax(10000);
Timer progressBarAdvancer = new Timer();
progressBarAdvancer.scheduleAtFixedRate(new TimerTask() {
public void run() {
progressBar.setProgress(progressBar.getProgress() + TIMER_FREQ);
}
},
0, //Delay before first execution
TIMER_FREQ);
By doing this, the progressBar operates on a separate thread from the recording, but will finish within the required 10 seconds. At this pount you can stop the recording and do the rest of the things.
Also, you can use the Video Trimmer based on "k4l-video-trimmer" library which handle various issues on the k4l-video-trimmer.
You can use mobile-ffmpeg Supports API Level 16+
fun scaleVideo(path: String, destinationFilePath: String) {
_loaderVisisble.value = true
viewModelScope.launch {
val cmd = arrayOf(
"-i",
path,
"-vf",
"scale=576:1024:force_original_aspect_ratio=decrease",
destinationFilePath
)
Log.v("str_Cmd", cmd.toString() + "")
val status = executeCommand(cmd)
when (status) {
FFmpeg.RETURN_CODE_SUCCESS -> {
_loaderVisisble.value = false
val mergedFile = File(destinationFilePath)
Log.v(
"target_file_size",
(mergedFile.length() / 1024).toString().toInt().toString() + ""
)
onVideoScaleListener.postValue(destinationFilePath)
}
FFmpeg.RETURN_CODE_CANCEL -> {
_loaderVisisble.value = false
}
else -> {
_loaderVisisble.value = false
}
}
}
}
private suspend fun executeCommand(cmd: Array<String>): Int {
var status = -1
withContext(Dispatchers.Default) {
val rc = FFmpeg.execute(cmd)
when (rc) {
FFmpeg.RETURN_CODE_SUCCESS -> {
Log.i(
Config.TAG,
"Command execution completed successfully."
)
}
FFmpeg.RETURN_CODE_CANCEL -> {
Log.i(
Config.TAG,
"Command execution cancelled by user."
)
}
else -> {
Log.i(
Config.TAG,
String.format(
"Command execution failed with rc=%d and the output below.",
rc
)
)
}
}
status = rc
}
return status
}

Categories