FFmpegMediaMetadataRetriever from External Storage? Android Studio/Kotlin - android

So I'm making my first Android app and I'm trying to get it to allow the user to pick a video from their gallery before seeing the video and the video's current details in the next activity.
My problem is that when I use FFmpegMediaMetadataRetriever and pass it the video's filepath, I receive the error java.lang.IllegalArgumentException: setDataSource failed: status = 0xFFFFFFFF.
I've heard through the grapevine that this means my filepath is invalid. When I Log.d the filepath, I get content://media/external/file/3565, which to me looks like a proper filepath!
I hope somebody can help me figure this out.
Here is my activity class for context:
class NewProject : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_project)
val videoPath = intent.getStringExtra("video")
initVideo(videoPath)
backButtonText.setOnClickListener{ goBack() }
}
private fun goBack() {
val intent = Intent(this,MainActivity::class.java)
startActivity(intent)
}
private fun initVideo(videoPath:String) {
newProjVideoView.setVideoPath(videoPath)
newProjVideoView.start()
newProjVideoView.setOnCompletionListener {
newProjVideoView.pause()
}
getVideoMetadata(videoPath)
}
private fun getVideoMetadata(videoPath: String) {
try {
e("videoPath", videoPath)
val receiver = FFmpegMediaMetadataRetriever()
receiver.setDataSource(videoPath)
} catch (e:IOException) {
e("retrieve1","There was an issue", e)
}
}
}
I'm also happy to hear any constructive feedback on my code!
Please, thank you and have a nice day!

So, I think my issue stemmed from trying to pass the video through an intent and then running the MetadataRetriever. I solved it by getting all the info in the previous activity before passing each value as an extra to be used on the next screen.

Related

Uploading image to firebase storage (KOTLIN)

To retrieve an image in the gallery, most tutorials show the usage of
startActivityForResult()
It is currently deprecated, I have found this Basics of Intents as a substitute. The code block does return a URI, however, when I use that uri to set an image or to upload to firebase storage, it does not work.
Flow of getting and uploading the image:
Click the profile picture
Pick from file storage or gallery
After choosing, it would automatically set the image in the view and upload it to firebase storage associated with the auth ID of the current logged in user.
I have this in a fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
CoroutineScope(Dispatchers.IO).launch {
sharedViewModel.uploadImage(uri)
}
binding.ivProfilePic.setImageURI(uri)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.ivProfilePic.setOnClickListener{
getContent.launch("image/*")
}
}
while this is the code for uploading to firebase:
private val storageRef = Firebase.storage.reference
fun uploadImage(path: Uri?){
val file = Uri.fromFile(File(path?.path!!))
storageRef.child("images/${UUID.randomUUID()}").putFile(file)
}
I think I am missing something here.
I appreciate the help.
I've found the solution!
for the registerActivity, I've used the photo picker support library link
I also added this dependency implementation 'androidx.activity:activity-ktx:1.6.1' as discussed in the documentation.
for the uploading to firebase I think I was just confused with the usage the File class and Uris. I could've passed the Uri at first, I was probably confused with the new keywords (File and Uri) and just jumped and the copy pasted the code.
this is the code I arrived at:
I've moved the registerForActivityResult code outside the onCreate() and just a child of the Fragment class.
private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
binding.ivProfilePic.setImageURI(uri)
sharedViewModel.uploadImage(uri)
} else {
// insert code for toast showing no media selected
}
}
on onViewCreated():
binding.ivProfilePic.setOnClickListener{
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
and for uploading to firebase:
I've just passed the Uri
fun uploadImage(path: Uri){
storageRef.child("images/${UUID.randomUUID()}").putFile(path)
.addOnSuccessListener {
// Display upload complete
}
}
thank you for the help

setOnClickListener is not showing the Toast or doing the Intent

this is in Kotlin, it doesn't seem to work no matter what I try it's just a button with a click listener. Its function is to take me to another activity, I tried a toast but didn't show either. I tried not using the function and also didn't work. and can we use this method with a text view? I'm new in Kotlin so easy on me...
here's the code
val startButton = binding.loginButton
startButton.setOnClickListener {
fun crtUser() {
Toast.makeText(this, "It's Working!", Toast.LENGTH_LONG).show()
val intent = Intent(this#LoginActivity, SignupActivity::class.java)
startActivity(intent)
}
crtUser()
}
}
I also used finish() after the Intent, and it crashed
Please do not use this syntax, it is not invalid, but improving it will be much better for you and for other developers to understand faster and easily what is going on :). Trigger the function from the on click event as follows:
fun onCreateOrOtherMethod() {
binding.button.setOnClickListener {
createUser()
}
}
And then you can have createUser() as an inner method of your current class:
private fun createUser() {
Toast.makeText(this, "It's Working!", Toast.LENGTH_LONG).show()
val intent = Intent(this#LoginActivity, SignupActivity::class.java)
startActivity(intent)
}
Little tip: don't abbreviate the methods names, you can use long name if needed as long as it improves the semantics of your code.
If it crashes, please, attach the Exception stack trace :D
It's working now just after I changed this
setContentView(R.layout.login_activity)
To this
setContentView(binding.root)
Any Idea Why??

Android 11 - CallRedirectionService not getting triggered

We need to invoke a service when an outgoing call is placed so that we can use the target number to show certain additional information. As per Android documentation, CallRedirectionService should be used. However, after declaring a custom service as depicted in documentation, we find that the custom service is not getting triggered. Please let us know what we are doing wrong. Appreciate your help.
I referred to this link as well but not clear on the answer. There is a mention to role acquisition but I did not find that in Android documentation. Please direct me to the relevant page if available.
CallRedirectionService Implementation not working
Manifest.xml
<service android:name="<mypackage>.CustomCallService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
Custom service code
#Override
public void onPlaceCall(#NonNull Uri handle, #NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse) {
System.out.println("Outgoing:" + initialPhoneAccount + ":" + handle); //Call does not reach here
placeCallUnmodified();
}
In Kotlin:
Your implementation of the CallRedirectionService seems correct. I understand that the only step missing is the role request and acquisition.
You can prompt the user to give you the CallRedirectionService role by using the RoleManager class.
In this example below, we are requesting this role as soon as the MainActivity is created:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isRedirection())
roleAcquire(RoleManager.ROLE_CALL_REDIRECTION)
}
}
The following functions shall be used:
private fun isRedirection(): Boolean {
return isRoleHeldByApp(RoleManager.ROLE_CALL_REDIRECTION)
}
private fun isRoleHeldByApp(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleHeld(roleName)
}
private fun roleAcquire(roleName: String) {
val roleManager: RoleManager?
if (roleAvailable(roleName)) {
roleManager = getSystemService(RoleManager::class.java)
val intent = roleManager.createRequestRoleIntent(roleName)
startActivityForResult(intent, 1)
} else {
Toast.makeText(
this,
"Redirection call with role in not available",
Toast.LENGTH_SHORT
).show()
}
}
private fun roleAvailable(roleName: String): Boolean {
val roleManager: RoleManager? = getSystemService(RoleManager::class.java)
return roleManager!!.isRoleAvailable(roleName)
}

Make Coroutine wait for Jsoup response in Kotlin

I'm kind of new developing with Android, and have problems understanding how things works. I made an app to scrape content from a page, and download elements, first with AsyncTask and worked, but since AsyncTask doesn't let me communicate with the UI Activity on progress, i decided to change to coroutines, checked an example, and the same code i used doesn't seems to work.
I used a few logs to try to determine the problem, and seems like it doesn't wait for the Jsoup request. The coroutine first calls a method scrapePage() to download the HTML and scrape the links, and then calls downloadImages() to add the links to Android's DownloadManager. In the logs Log.d("action", "Start Scraping") is printed, but Log.d("action", "Page downloaded") doesn't, still we get Log.d("action", "End") from the coroutine, which makes me think that instead of waiting for the Jsoup request to answer, it goes with an empty response, causing the rest of the code to not work correctly.
DownloadService.kt
object DownloadService {
private val parentJob = Job()
...
private val coroutineScope = CoroutineScope(Dispatchers.Main + parentJob +
coroutineExceptionHandler)
fun StartService(URL: String, location:String, contx:Context) {
coroutineScope.launch(Dispatchers.Main) {
Log.d("action", "Start")
val links = scrapePage(URL)
val download = downloadImages(links, location, contx)
Log.d("action", "End")
}
}
private suspend fun scrapePage(url: String): MainActivity.Scraped =
withContext(Dispatchers.IO) {
var URL = url
var scrape = MainActivity.Scraped()
try {
Log.d("action", "Start Scraping")
var response = Jsoup.connect(URL).get()
Log.d("action", "Page downloaded")
response.getElementsByClass("link").forEach {
/*Scrape URLs*/
Log.d("action", "Add "+link)
}
} catch (e: Exception) {
when(e) {
is HttpStatusException -> {
System.out.println(e.getStatusCode())
scrape.error = true
error = true
}
}
}
return#withContext scrape
}
...
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
...
fun makeRequest(URL : String) {
WorkingURL = URL
var uri = ""
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI.toString()
log.text = uri
} else {
uri = getStoragePath()
log.text = uri
}
DownloadService.StartService(URL, uri, this)
Log.d("links", DownloadService.getError().toString())
}
}
I am not sure where the problem is, nor where to start searching. I know the code for the Scraping works, because i used it before with AsyncTask, so the problem seems to be passing it to coroutines.
Here Jsoup.connect(URL).get() is throwing an error. so, Log.d("action", "Page downloaded") is not called.
But since you are handling the exception, the code runs the catch part and completes the suspend function and moves on to downloadImages().
Solution
First, add a log in the catch part of scrapePage() function and find out what is causing the exception. Everything else in your code is good.

Kotlin variable getting initialized twice

This is the function that gets the object in MyHttpsClient.java.
public static MyHttpsClient getClient(Context context, OnHttpsResult resultListener) throws Exception {
Log.i("TAG","---------------------------" + context.getClass().getName() + " getClient!--------------------------------");
for(String c : contextNames){
if(c == context.getClass().getName()){
throw new Exception("Can not be initialized twice in the same class");
}
}
contextNames.add(context.getClass().getName());
return new MyHttpsClient(context, resultListener);
}
I initialize it in Login.kt like this:
object MyHttpsListener : MyHttpsClient.OnHttpsResult{
override fun OnResult(jsonObject: JSONObject?) {
when(workid){
0 -> {
var status = jsonObject?.getInt("status")
if(status == 1){
instance.startActivity(Intent(instance, MainActivity::class.java))
}
else{
Toast.makeText(instance, "Incorrect email or password!", Toast.LENGTH_LONG).show()
}
instance.pb_login.visibility = View.GONE
}
}
}
}
var client = MyHttpsClient.getClient(this, MyHttpsListener)
After running app, I found that the "client" called
"getClient()" twice, so the app crashed.
Finally, I solved the problem in this way:
lateinit var client: MyHttpsClient
And the "client" initialized in onCreate
override fun onCreate(savedInstanceState: Bundle?) {
...
client = MyHttpsClient.getClient(this, MyHttpsListener)
...
}
But I don't know why this happened. I'm looking forward to someone who can help me.
If you need to create a conection (MyHttpsClient) and you don't look to the connection you've already made, you can create multiple instance for the same conection and, in fact, for the same object.
The best way is to look for the singleton pattern which is a good patern to restrict number of conection and so the number of instance.
Thanks a lot for taking the time to check my question. I am visited stackoverflow first time. Finally I found out my problem, I mistakenly initialized it at the top. And I will show the complete code next time..
private var instance : Login = Login()
class Login : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {

Categories

Resources