For the pop-up mic permissions prompt

edited December 2017 in Android Mode

In an app that I developed, it requires the mic permission of the user. I've added the RECORD_AUDIO permissions in the Manifest file. Will that result in the permissions prompt to pop up at run time or is there anything else I need to do like add the getPermissionToRecordAudio() and onRequestPermissionsResult() code to MainActivity.java , etc? If so, where is the MainActivity.java in the Processing sketch documents? Do I have to use Android Studio for that? Pl let me know. Thanks.

Answers

  • edited December 2017

    @sonia===

    • if you are declaring "dangerous" permissions in the manifest there will be a popup at each time that the user launches the app

    • if you write code for asking on runtime the difference will be that you can "explain" why this permission is necessary and prevent the user (with a dialog) that some features will not run; in this case also you can add "alternative" to your app

    • in any case if >=API 23 the user can change his mind....

    • with P5 you cannot modify the MainActivity launcher class; you are in a fragment but you can write code for runtime permissions even in a fragment.

    • if you want to modify the MainActivity export your app and import it in AS or Eclipse.

  • I've already declared the dangerous permission in the manifest. But, that doesn't open up the pop-up. Instead, I've to manually go to settings and turn on the microphone permissions.

    Secondly, I'm trying to add requestPermission() in the setup. That does prompt the user and also changes the microphone settings in the app. But, instead of starting to run, the app crashes. How do I get around it? Thanks.

  • Update: requestPermission("android.permission.RECORD_AUDIO") prompts the user, but once allowed crashes. However, after closing the app, when I open again on the phone, it works. So, when I run the app, it asks for permission and crashes. But, when i click the installed app on the phone, it works.

    Does that mean that when the user downloads and installs the app, the prompt will ask and work? How will I know?

  • edited December 2017

    @sonia===

    can you be more precise about:

    • your version of P5 android mode (because i have tested with 3, not with 4)

    • your target minSDK

    • your phone OS

    • knowing that the system permissions for API was changed since API 23:

    from the android doc=

    The way Android makes the requests depends on the system version, and the system version targeted by your app:

    If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it accesses permission-protected APIs. For more information about requesting permissions in your app, see the Working with System Permissions training guide.
    If the device is running Android 5.1.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app. If you add a new permission to an updated version of the app, the system asks the user to grant that permission when the user updates the app. Once the user installs the app, the only way they can revoke the permission is by uninstalling the app.
    
    • so (according to your aims) when you ask for permissions (runtime) you have to write code with if (buildSDK.....) {}else{}
    1. Processing 3.3.6, android mode - 4.0
    2. minSDK - 17, targetSDKVersion - 26
    3. Panasonic P55 Novo 4G, android OS
    4. Yes, API 23 and above, permissions changed.

    I need to figure out the code to write for run time permissions with record audio. For now, I just tried with requestPermission(...) and that simply brings about a pop up and pressing allow does open up the microphone. But, am not able to go beyond that. My code is this:

    import android.media.AudioRecord;
    import android.media.AudioFormat;
    import android.media.MediaRecorder;
    
    void setup() {
    
      size(displayWidth, displayHeight);
      f= createFont( "Arial", 16, true);
      requestPermission("android.permission.RECORD_AUDIO");
      drawBaseLine = displayHeight-150;
      minBufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);
    
    if (minBufferSize == AudioRecord.ERROR_BAD_VALUE)  {
        RECORDER_SAMPLERATE = 8000; // forced by the android emulator
        MAX_FREQ = RECORDER_SAMPLERATE/2;
        bufferSize =  getHigherP2(RECORDER_SAMPLERATE);
    
     buffer = new short[bufferSize];
    audioRecord = new AudioRecord( MediaRecorder.AudioSource.VOICE_RECOGNITION, RECORDER_SAMPLERATE,
                                     RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
    

    Once I request permission to record, how do I use the 'allow' press to dictate the audioRecord object? Its getting confusing now. Or should I use the conditional later here?

    if ((audioRecord != null) && (audioRecord.getState() == AudioRecord.STATE_INITIALIZED)) {
        try {
    
          audioRecord.startRecording(); 
          aRecStarted = true;
        }
        catch (Exception e) {
          aRecStarted = false;
        }
      }
    
  • edited December 2017

    @sonia===

    i thought that your problem was for permissions && seeing your previous posts i tested the code i have already used for that in eclipse, with some changes for P5; it runs in a standard way, asking the user to give permissions (in my snipet for RECORD_AUDIO, though i think that recording you have perhaps also to add WRITE_EXTERNAL_STORAGE); not any crash, neither at launch nor at resuming. Try the code below.

    if now your question is about recording close this post and open another one.

    CODE SNIPPET PERMISSIONS ON RUN TIME RECORD_AUDIO ADDED IN THE MANIFEST p5 .3.3, android mode 3 nexus 7 running 6.1

        import android.app.Activity;
        import android.Manifest;
        import android.content.pm.PackageManager;
        import android.os.Build;
    
    
        int maVersion = Build.VERSION.SDK_INT;
        Activity act;
    
        void setup(){
    
          size(800,600);
          background(255,0,0);
          act = this.getActivity();
    
          if (maVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {//verifier API
                        if (!permissionsDejaAccordees()) {//ne pas redemander si la permission a déjà été accordée
                            demandePermissionParticuliere();//sinon, demander....
                        }
        }
    
    
        };
    
    
        void draw(){
    
    
        };
    
        private boolean permissionsDejaAccordees() {
    
            int result = act.checkSelfPermission( Manifest.permission.RECORD_AUDIO);
            if (result == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                return false;
            }
        };
    
        private void demandePermissionParticuliere()  {
    
                act.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 101);//+WRITE_EXTERNAL_STORAGE????
        };
    
    
    
        //RESULTAT:
    
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            switch (requestCode) {
                case 101:
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                       println("permissions accordées");////now you can open your microphone
    
                    } else {
                        println("permissions not granted");
                    }
                    break;
                default:
                    act.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    
  • Thanks. I'll integrate this snippet with my source code and check.

    My question is about mic permissions during run time. There is no recording here since the app is like a real time audio visualizer. So no storage of audio files and so on.

  • This is a fragment of the code:

            import android.media.AudioRecord;
            import android.media.AudioFormat;
            import android.media.MediaRecorder;    
    
            int       RECORDER_SAMPLERATE = 44100;
            int       MAX_FREQ = RECORDER_SAMPLERATE/2;
            final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
            final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
            final int PEAK_THRESH = 20;
    
            short[]     buffer           = null;
            int         bufferReadResult = 0;
            AudioRecord audioRecord      = null;
            boolean     aRecStarted      = false;
            int         bufferSize       = 2048;
            int         minBufferSize    = 0;
            float       volume           = 0;
            FFT         fft              = null;
            float[]     fftRealArray     = null;
            int         mainFreq         = 0;
    
            float       drawScaleH       = 4.5; // TODO: calculate the drawing scales
            float       drawScaleW       = 1.0; // TODO: calculate the drawing scales
            int         drawStepW        = 2;   // display only every Nth freq value
            float       maxFreqToDraw    = 2500; // max frequency to represent graphically
            int         drawBaseLine     = 0;
    
            PFont f;
    
            void setup() {
    
              size(displayWidth, displayHeight);
              f= createFont( "Arial", 16, true);
              requestPermission("android.permission.RECORD_AUDIO");
              drawBaseLine = displayHeight-150;
              minBufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);      
              if (minBufferSize == AudioRecord.ERROR_BAD_VALUE)  {
                RECORDER_SAMPLERATE = 8000; 
                MAX_FREQ = RECORDER_SAMPLERATE/2;
                bufferSize =  getHigherP2(RECORDER_SAMPLERATE);        
    
              } else bufferSize = minBufferSize;      
              buffer = new short[bufferSize];  
    
    
              audioRecord = new AudioRecord( MediaRecorder.AudioSource.VOICE_RECOGNITION, RECORDER_SAMPLERATE,
                                             RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
    
              //audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE,
               //                              RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
              if ((audioRecord != null) && (audioRecord.getState() == AudioRecord.STATE_INITIALIZED)) {
                try {          
                  audioRecord.startRecording(); 
                  aRecStarted = true;
                }
                catch (Exception e) {
                  aRecStarted = false;
                }
              }
                if (aRecStarted) {
                    bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
                    // compute nearest higher power of two
                   bufferReadResult = getHigherP2(bufferReadResult);
                    fft = new FFT(bufferReadResult, RECORDER_SAMPLERATE);
                    fftRealArray = new float[bufferReadResult]; 
                    drawScaleW = drawScaleW*(float)displayWidth/(float)fft.freqToIndex(maxFreqToDraw);           
                }      
              fill(0);
              noStroke();
            }
    
            void draw() {
               background(128); fill(0); noStroke();
               if (aRecStarted) {
                   bufferReadResult = audioRecord.read(buffer, 0, bufferSize);   
    
                   volume = 0;
                   for (int i = 0; i < bufferReadResult; i++) {
                        fftRealArray[i] = (float) buffer[i] / Short.MAX_VALUE;// 32768.0;
                        volume += Math.abs(fftRealArray[i]);
                   }
                   volume = (float)Math.log10(volume/bufferReadResult);
                   }  else {
                fill(255,0,0);
            text("AUDIO RECORD NOT INITIALIZED!!!", 100, height/2);
          }
    

    Like I mentioned, I'm a novice. Pl bear with me. I integrated your snippet like this:

    import android.media.AudioRecord;
    import android.media.AudioFormat;
    import android.media.MediaRecorder;
    import android.app.Activity;
    import android.Manifest;
    import android.content.pm.PackageManager;
    import android.os.Build;
    
    int maVersion = Build.VERSION.SDK_INT;
    Activity act;
    int       RECORDER_SAMPLERATE = 44100;
              int       MAX_FREQ = RECORDER_SAMPLERATE/2;
                final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
                final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
                final int PEAK_THRESH = 20;
    
                short[]     buffer           = null;
                int         bufferReadResult = 0;
                AudioRecord audioRecord      = null;
                boolean     aRecStarted      = false;
                int         bufferSize       = 2048;
                int         minBufferSize    = 0;
                float       volume           = 0;
                FFT         fft              = null;
                float[]     fftRealArray     = null;
                int         mainFreq         = 0;
    
                float       drawScaleH       = 4.5; // TODO: calculate the drawing scales
                float       drawScaleW       = 1.0; // TODO: calculate the drawing scales
                int         drawStepW        = 2;   // display only every Nth freq value
                float       maxFreqToDraw    = 2500; // max frequency to represent graphically
                int         drawBaseLine     = 0;
    
                PFont f;
    
    void setup(){
    
      size(displayWidth, displayHeight);
      f= createFont( "Arial", 16, true);
      act = this.getActivity();
    
      if (maVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {//verifier API
                    if (!permissionsDejaAccordees()) {//ne pas redemander si la permission a déjà été accordée
                        demandePermissionParticuliere();//sinon, demander....
                    }
    }
    drawBaseLine = displayHeight-150;
                  minBufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);      
                  if (minBufferSize == AudioRecord.ERROR_BAD_VALUE)  {
                    RECORDER_SAMPLERATE = 8000; 
                    MAX_FREQ = RECORDER_SAMPLERATE/2;
                    bufferSize =  getHigherP2(RECORDER_SAMPLERATE);        
    
                  } else bufferSize = minBufferSize;      
                  buffer = new short[bufferSize];  
    
    
                  audioRecord = new AudioRecord( MediaRecorder.AudioSource.VOICE_RECOGNITION, RECORDER_SAMPLERATE,
                                                 RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
    
                  //audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE,
                   //                              RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
                  if ((audioRecord != null) && (audioRecord.getState() == AudioRecord.STATE_INITIALIZED)) {
                    try {          
                      audioRecord.startRecording(); 
                      aRecStarted = true;
                    }
                    catch (Exception e) {
                      aRecStarted = false;
                    }
                  }
                    if (aRecStarted) {
                        bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
                        // compute nearest higher power of two
                       bufferReadResult = getHigherP2(bufferReadResult);
                        fft = new FFT(bufferReadResult, RECORDER_SAMPLERATE);
                        fftRealArray = new float[bufferReadResult]; 
                        drawScaleW = drawScaleW*(float)displayWidth/(float)fft.freqToIndex(maxFreqToDraw);           
                    }      
                  fill(0);
                  noStroke();
                }
                }
    void draw() {
    }
    
    private boolean permissionsDejaAccordees() {
    
        int result = act.checkSelfPermission( Manifest.permission.RECORD_AUDIO);
        if (result == PackageManager.PERMISSION_GRANTED) {
            return true;
        } else {
            return false;
        }
    };
    
    private void demandePermissionParticuliere()  {
    
            act.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 101);//+WRITE_EXTERNAL_STORAGE????
    };
    
    
    
    //RESULTAT:
    
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 101:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                   println("permissions accordées");////now you can open your microphone
    
                } else {
                    println("permissions not granted");
                }
                break;
            default:
                act.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }   
    

    When I run the app, I get the same error: Unfortunately your app has stopped. I close it. But when I open the launched app again, it works and when I check the settings, the mic is on. Is my integrating wrong? Do I have to connect the permissions code with initializing the audioRecord object or something like that?

  • Phone has android version 6.0, android mode 4.0 in Processing. API Level - 23, according to https://stackoverflow.com/questions/3993924/get-android-api-level-of-phone-currently-running-my-application

  • edited December 2017

    Called the onRequestPermissionsResult() function with the conditional before doing my stuff in the draw() like this:

    void draw() {
    background(128); fill(0); noStroke();
    if (onRequestPermissionsResult (101, Manifest.permission.RECORD_AUDIO, grantResults)) {
         if (aRecStarted) {
             bufferReadResult = audioRecord.read(buffer, 0, bufferSize);   
    
             volume = 0;
             for (int i = 0; i < bufferReadResult; i++) {
                  fftRealArray[i] = (float) buffer[i] / Short.MAX_VALUE;// 32768.0;
                  volume += Math.abs(fftRealArray[i]);
             }
             volume = (float)Math.log10(volume/bufferReadResult);
             }  else {
          fill(255,0,0);
      text("AUDIO RECORD NOT INITIALIZED!!!", 100, height/2);
    }}
    

    The last argument (int array grantResults) says 'variable does not exist'. Tried 0, grantResults[0], PERMISSION_GRANTED, etc., (as per developer.android site) not working. What should be passed as that int[] array?

  • @sonia===

    Have you tested my snippet alone??? Does it works? Have you added the reor_audio permissionto your Manifest?

    Now that i have understood what you re trying to do i ll' give a look to your code ASAP; yet, i am sure that the way you are asking for permissions in your last post is wrong.

  • edited December 2017 Answer ✓

    @sonia===

    i have tested your code + mine one that i have added.

    as for permissions: not any problem, it works, asking the first time then no more asking (till the user changes his mind!)

    as for record audio i cannot test (&& help to solve) because a) i dont see what is your import for fast fourier FFT b) there is not any method called getHigherP2() in your post

    as for your error (last post) it is because you dont understand what this int Array is :an array made by the system when the user says "yes"; the request code can be any arbitrary int (i put 101, it could be 376!) - In your case as there is only 1 permission, the call back is at the [0] of the array, that s all...

  • Yes, tested your snippet alone and it works. Added record audio permissions to the manifest (otherwise the app will not run). I'm trying to figure out calling the function onRequestPermissionsResult(...). Here is the code for the method getHigherP2():

    int getHigherP2(int val)
    {
      val--;
      val |= val >> 1;
      val |= val >> 2;
      val |= val >> 8;
      val |= val >> 16;
      val++;
      return(val);
    }
    
  • Answer ✓

    @sonia===

    • have you your own FFT class?

    • in setup() or in start() you check permissions state with checkSelfPermissions (see my boolean method); if "true"______nothing to do; else: "ask for some permission" with requestPermissions (my2° method)---- then: do nothing, the third method is a callBackfrom the second one

    • create a boolean "permissionOK" to false

    • when it' s true initialize your audioRecord, that s all...

  • Got it. Its working now. Thanks.

  • Meanwhile, I just came across your answer to view keyboard in android, that is, how to open a virtual keyboard on touch. Is there any inputmethodmanager to go to the next screen (featuring the main app) when a button named NEXT is touched on the first welcome page? Thanks.

  • @sonia===

    for that open a new discussion tagged with android keyboard (it is for forums users when they are looking for some precise topic).

  • @sonia===

    tested with your FFT class extending the abstract one: everything runs ok,though you draw nothing on screen...except the red text (change the textSize()).

Sign In or Register to comment.