Android-runtime: Android MediaRecorder.AudioSource = undefined

Created on 25 Feb 2016  路  24Comments  路  Source: NativeScript/android-runtime

_From @bradmartin on February 24, 2016 4:29_

MediaRecorder.AudioSource -

Android Sample:

    private void startRecording() {
    mRecorder = new MediaRecorder();
    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mRecorder.setOutputFile(mFileName);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

    try {
        mRecorder.prepare();
    } catch (IOException e) {
        Log.e(LOG_TAG, "prepare() failed");
    }

    mRecorder.start();
}

`

NativeScript JS code:

    var MediaRecorder = android.media.MediaRecorder;
    var AudioSource = android.media.MediaRecorder.AudioSource;
    var OutputFormat = android.media.MediaRecorder.OutputFormat;
    var AudioEncoder = android.media.MediaRecorder.AudioEncoder;

   var recorder;

   function startAudio(args) {
      try {
          console.log('START AUDIO');
          recorder = new MediaRecorder();
          console.log('RECORDER: ' + recorder);

         // *** FAILS with AudioSource being undefined. **
         recorder.setAudioSource(AudioSource.MIC);
         recorder.setOutputFormat(OutputFormat.THREE_GPP);
         recorder.setOutputFile("bradTest");
         recorder.setAudioEncoder(AudioEncoder.AMR_NB);
         recorder.prepare();
         recorder.start();
       } catch (ex) {
          console.log(ex);
      }
 }
 exports.startAudio = startAudio;

`

_Copied from original issue: NativeScript/NativeScript#1622_

medium bug breaking change

Most helpful comment

Ping @tjvantoll , @EddyVerbruggen , @sitefinitysteve, @bradmartin, @AntonioCuevaUrraco, @hdeshev , @vakrilov , @PeterStaev, @alexziskind1, @emiloberg, @PanayotCankov , @TobiasHennig, @NathanaelA, @ginev
Hi all, we need you opinion on this matter, because this will affect you, as well as all other nativescript users. I'm trying to start a dialog, where everyone will be satisfied with the final result.

All 24 comments

_From @bradmartin on February 24, 2016 6:39_

Also I've added the RECORD_AUDIO permission so that's not the issue.

Hey @slavchev can you help here? It seems that this class is not exposed for some reason.

Hi @bradmartin

This is a bug in our runtime. While we have all the metadata it turned out that we don't set static members for the inner classes. As a temporary workaround I would suggest to use the constant values (e.g. MIC=1, VOICE_UPLINK=2, etc.)

@bradmartin

Your issue raised an interesting question. Suppose the following Java code

package com.example;

public class A {
   public class B {
      public int MY_CONST = 1;
   }
}

// option 1
A.B b1 = new A().new B();

// option 2
A a = new A();
A.B b2 = a.new B();

As we can see there are two options (think syntax) to create an instance of A.B class. In NativeScript this is supported as follows

// option 1
var b1 = new new com.example.A().B(); // note the two "new" operators

// option 2
var a = new com.example.A();
var b2 = new a.B();

Anyway, the point is that we attach B constructor function to an instance of A. So we don't support the following syntax (which is valid Java syntax)

com.example.A.B.MY_CONST

Proposal 1

We can attach the static members of B to A. Think of the following

com.example.A.B = { MY_CONST: 1 }
// typeof(com.example.A.B) === 'object'
// typeog((new com.example.A()).B) === 'function'

Proposal 2

We can attach the inner constructor function to the outer one. Think of the following

com.example.A.B === (new com.example.A().B) // true

However doing this will provide a new possible syntax.

//option 3
var a = new com.example.A();
var b = new com.example.A.B(a);

I am not sure which one is less confusing. In the first proposal there are two B - one object and one constructor function. In the second proposal it will be possible to create instance of B by explicitly passing instance of A. I guess the second one is not a bad solution but I would like to hear other opinions.

@slavchev thanks for confirming the bug for now. As for your proposals, I like _Proposal 2_. However, if using the Constants INT value works, then personally I would rarely use these alternatives. Using the INT values is much less code and cleaner to me. Might be useful to hear from some other devs who might be familiar with the inner classes to see what they think.

Ping :+1: @NathanaelA @tjvantoll @NathanWalker @sitefinitysteve @EddyVerbruggen

As far as syntax goes Proposal 1 looks great, but I feel it might not cover all general purpose scenarios.
I am more inclined towards implementing Proposal 2 because it's the logical thing to do in our case, and although it wont have as pretty syntax it would be logically right and it will treat the problem in a more general way. Further more java .class file do exactly that under the hood (creating instance of B explicitly passing an instance of A)

One note:
as per the current example

public class A {
public class B {
public int MY_CONST = 1;
}
}

com.example.A.B.MY_CONST

is not a valid syntax. MY_CONST should be made static final but I guess that what is meant by the name.

I don't like Proposal 2 since it does not "explain" why I need an A instance to create an instance of B. It's just not communicated well. If I have a static path to B why I need an A to create B.
Besides in Java you don't have access to the B constructor through A but through an instance of A.

@Plamen5kov

but I feel it might not cover all general purpose scenarios

Can you elaborate more.

Also in the Android Docs there will be no word of a B(A a) constructor making the developer unsure which class needs this and which class don't. Cause B may be a public static class B. Which will not require an instance of A.

public class A {
public static class B {
public static final int MY_CONST = 1;
}
}

B b = new A.B();

and then I need to check every now and then every nested class in the Android source/docs before use to be sure.

@blagoev
what I can think of right now is a simple inheritance:

public class OuterClass
{
    private class A {
        public static final int BASE_INT = 42;
    }

    public class B extends A {
    }
}

then, if we chose proposal 1, we will need to traverse the base class A of B and find it's static members so we can attach them to OuterClass.IMO augmenting OuterClass fields with A's variables, does not seem like a generally good solution.

My understanding of Proposal 1 is to attach A and B to OutClass like so
x.y.z.OuterClass.A.B.MY_B_CONST and x.y.z.OuterClass.A.MY_A_CONST.

The exact mechanism of how it is done is not relevant to the developer even if it means more work on our side. It is the final result that matters. And the final result should be to stick with the Android docs and public API as much as possible. so having x.y.z.OuterClass.MY_A_CONST and x.y.z.OuterClass.MY_B_CONST is never good .

I have no problems with attaching all constants to the outer class. I'm worried that we might miss one or many corner cases that will be resolved by them self, if we follow the way java works. I don't believe it's a good idea to add corner cases to our original implementation, when we can have a more general solution. It's true that for now I have no particular user scenario in mind besides inherited static fields, but I believe the more general the solution the better.

One issue with Proposal 1 is about the type equality checks
It is related to this

In the first proposal there are two B - one object and one constructor function.

I think this comes from the fact the the original suggestion was to implement the static path using an Object of type B. That has all static properties like so

com.example.A.B

and then this will need a constructor function property B of every A instance to be able to create B instances.

In the above implementation case indeed there is a problem when comparing some B instance to A.B

var b = new new com.example.A().B();
b instance of A.B // FALSE

this will be expected by the developer because A.B.MY_CONST is working fine.

The issue with Problem 1 and Problem 2 is common. If we expose static path to B through A it means someone will expect "new A.B" to work or "b instance of A.B" to work meaning that Proposal 2 Option 3 syntax for creating B instances must be supported.

In fact currently there is no way to check b instanceof A.B without an instance of A. In Java that is possible.

So it seems that Proposal 2 and options 3 are inevitable.

Also, we have to think about .class, .extend and .null pseudo static members. ping @Pip3r4o

yes forgot about that... another reason for exposing the ctor function B directly through A for all nested classes.

Ping @tjvantoll , @EddyVerbruggen , @sitefinitysteve, @bradmartin, @AntonioCuevaUrraco, @hdeshev , @vakrilov , @PeterStaev, @alexziskind1, @emiloberg, @PanayotCankov , @TobiasHennig, @NathanaelA, @ginev
Hi all, we need you opinion on this matter, because this will affect you, as well as all other nativescript users. I'm trying to start a dialog, where everyone will be satisfied with the final result.

From reading the discussion up to this point it does seem like there is much choice and the Prop 2 is the best solution. One thing I wasn't sure of was would MY_CONST be accessible like A.B.MY_CONST under proposition 2? The constants are probably going to be the most used, so it should be usable as close to the Android docs as possible...

I'm not such a Java expert but since you say that inside the compiler does what Proposal 2/Option 3 will do, I also agree that this seems the best approach.
But I also agree with @NathanaelA comment above that static members (constants in most cases) should be usable by directly using A.B.MY_CONST

I'd like to give notice that the changes according to Proposal 2/option 3 will require that when instantiating inner class, a reference to the outer _this_ be provided as the first argument, even when you have just a default, or empty, constructor.

This also means that the syntax supported up to this moment for creating instances of inner classes will be removed from the runtime.

tl;dr :
Given the following class and its inner class

public class OuterClass {
    public class InnerClass {
        public static final String NAME = "John";
    }
}

You will no longer be able to do the following

var oC = new OuterClass();
var iC = new oC.InnerClass();

// OuterClass.InnerClass == undefined

And will instead have to write it like this

var oC = new OuterClass();
var iC = new OuterClass.InnerClass(oC);

OuterClass.InnerClass.NAME == iC.NAME  // "John"

Ping @tjvantoll , @EddyVerbruggen , @sitefinitysteve, @bradmartin, @AntonioCuevaUrraco, @hdeshev , @vakrilov , @PeterStaev, @alexziskind1, @emiloberg, @PanayotCankov , @TobiasHennig, @NathanaelA, @ginev giving you a heads up, SYNTAX CHANGE that may or may not affect you.

If approved, the changes will be available in the release following Augusts's, and I need to make sure that they are well communicated. Thank you!

Can't we make the com.example.A.B a constructor function that gets instance of A as first arg, but also preserve the current syntax, where instances of A have property B that can be used to instantiate B?

@Pip3r4o
One question just to clarify; how do you get access to the static items like in android you would do:
int MIC = MediaRecorder.AudioSource.MIC;
I realize in 2.1 and below this is broken, so under this change what will the equivalent in NS now?

@NathanaelA access to static items of inner classes will now be direct, and accessing MediaRecorder.AudioSource.MIC will work as expected. See the example in my previous comment

@Pip3r4o --
Thanks for the clarification; and I have no idea why I totally overlooked the last line of your example, it was there for me to see. ;-) So this is in 2.2?

@NathanaelA, it won't be in 2.2, because even though the implementation is done, we were hoping to hear an opinion from more people in the community, before finally deciding this is the syntax we want to use.
Up until now we supported the java syntax as much as possible to keep things as close to the native side as possible, but this syntax is more close to javascript than java IMO.

We will probably include this feature in 2.3.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Plamen5kov picture Plamen5kov  路  4Comments

atanasovg picture atanasovg  路  3Comments

Natalia-Hristova picture Natalia-Hristova  路  3Comments

m-abs picture m-abs  路  3Comments

triniwiz picture triniwiz  路  4Comments