Search code examples
androidspeech-recognitionvoice-recognitionspeech

Disable permissions for a certain activity within an app


I am creating a simple app. What I have currently found is that when I open my app, the following permission have been set in the .gradle folder:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

However my app has a second activity called Gvoice.java which then relies on googles speech to text.

What I have found is that if I keep the above permissions in the .gradle folder my main activity window works perfectly with pocketsphinx. However my Gvoice activity gives me a message 'Can't open microphone'.

But what I have found is that if I remove the three permissions in the .gradle folder. My main activity no longer does what I want it to do but my second activity called Gvoice now correctly converts speech to text and does not give me the 'Can't open microphone' message.

Is there an easy way to fix this issue as I believe it has definitely got to be related to microphone permissions. Please also note that whenever I return to the main activity the permissions need to be set and when I am in another activity outside of the main activity it needs to disable the permission (or alternatively if there is another way of doing this).

Can different permissions be set for different activities in the manifest file?

Below I have updated what I have got so far please have a look:

public class PocketSphinxActivity extends Activity implements RecognitionListener
{

private static final String KWS_SEARCH = "wakeup";

/* Keyword we are looking for to activate menu */
private static final String KEYPHRASE = "open voice command";   //adjust this keyphrase here and in string!

private SpeechRecognizer recognizer;
private HashMap<String, Integer> captions;

ListView lv;
TextView tv;
EditText a;
Button b;
Button c;

Boolean isDone = false;

@Override
public void onCreate(Bundle state) {
    super.onCreate(state);

    // Prepare the data for UI
    captions = new HashMap<String, Integer>();
    captions.put(KWS_SEARCH, R.string.kws_caption);
    setContentView(R.layout.main);
    ((TextView) findViewById(R.id.caption_text))
            .setText("Preparing the recognizer");

    lv = (ListView) findViewById(R.id.lvVoiceReturn);
    tv = (TextView) findViewById(R.id.result_text);
    a = (EditText) findViewById(R.id.TFusername);
    b = (Button) findViewById(R.id.bVoice);
    c = (Button)findViewById(R.id.Blogin);

    // Recognizer initialization is a time-consuming and it involves IO,
    // so we execute it in async task

    new AsyncTask<Void, Void, Exception>() {
        @Override
        protected Exception doInBackground(Void... params) {
            try {
                Assets assets = new Assets(PocketSphinxActivity.this);
                File assetDir = assets.syncAssets();
                setupRecognizer(assetDir);
            } catch (IOException e) {
                return e;
            }
            return null;
        }

        @Override
        protected void onPostExecute(Exception result) {
            if (result != null) {
                ((TextView) findViewById(R.id.caption_text))
                        .setText("Failed to init recognizer " + result);
            } else {
                switchSearch(KWS_SEARCH);
            }
        }
    }.execute();
//line added.../////////////////////////
    a.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (s.toString().trim().equalsIgnoreCase("open voice command")) {
                //
                //Do your stuff here OR button.performClick()
                //

                //DELAY
                Handler handler = new Handler();
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (!isDone) {
                            b.performClick();
                            isDone = true;
                        }
                    }
                }, 500);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });
////////////////////////////////////////
}

@Override
public void onDestroy() {
    super.onDestroy();
    recognizer.cancel();
    recognizer.shutdown();
}

/**
 * In partial result we get quick updates about current hypothesis. In
 * keyword spotting mode we can react here, in other modes we need to wait
 * for final result in onResult.
 */
@Override
public void onPartialResult(Hypothesis hypothesis) {
    if (hypothesis == null)
        return;

    String text = hypothesis.getHypstr();
    //((TextView) findViewById(R.id.result_text)).setText(text);
    ((EditText) findViewById(R.id.TFusername)).setText(text);
}

/**
 * This callback is called when we stop the recognizer.
 */
@Override
public void onResult(Hypothesis hypothesis) {
    //((TextView) findViewById(R.id.result_text)).setText("");
    ((EditText) findViewById(R.id.TFusername)).setText("");
    if (hypothesis != null) {
        String text = hypothesis.getHypstr();
        makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();


    //a.setText((String) tv.getText());
        //tv = TextView.getText().toString();
    }
}

@Override
public void onBeginningOfSpeech() {
}

/**
 * We stop recognizer here to get a final result
 */
@Override
public void onEndOfSpeech() {
    if (!recognizer.getSearchName().equals(KWS_SEARCH))
        switchSearch(KWS_SEARCH);
}

private void switchSearch(String searchName) {
    recognizer.stop();

    // If we are not spotting, start listening with timeout (10000 ms or 10 seconds).
    if (searchName.equals(KWS_SEARCH))
        recognizer.startListening(searchName);
    else
        recognizer.startListening(searchName, 10000);

    String caption = getResources().getString(captions.get(searchName));
    ((TextView) findViewById(R.id.caption_text)).setText(caption);
}

private void setupRecognizer(File assetsDir) throws IOException {
    // The recognizer can be configured to perform multiple searches
    // of different kind and switch between them

    recognizer = defaultSetup()
            .setAcousticModel(new File(assetsDir, "en-us-ptm"))
            .setDictionary(new File(assetsDir, "cmudict-en-us.dict"))

                    // To disable logging of raw audio comment out this call (takes a lot of space on the device)
            .setRawLogDir(assetsDir)

                    // Threshold to tune for keyphrase to balance between false alarms and misses
            .setKeywordThreshold(1e-45f)

                    // Use context-independent phonetic search, context-dependent is too slow for mobile
            .setBoolean("-allphone_ci", true)

            .getRecognizer();
    recognizer.addListener(this);

    /** In your application you might not need to add all those searches.
     * They are added here for demonstration. You can leave just one.
     */

    // Create keyword-activation search.
    recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);

}

@Override
public void onError(Exception error) {
    ((TextView) findViewById(R.id.caption_text)).setText(error.getMessage());
}

@Override
public void onTimeout() {
    switchSearch(KWS_SEARCH);
}

//Assign button clicks to go to a new activity:
public void onButtonClick_1(View v){
    if (v.getId() == R.id.bVoice){
        String str_1 = a.getText().toString();

        //Go to the relevant page if any part of the phrase or word entered in the 'EditText' field contains 'command' which is not case sensitive
        if (str_1.toLowerCase().contains("command")) {
            Intent userintent = new Intent(PocketSphinxActivity.this, Gvoice.class);

            startActivity(userintent);
        } else {
            Toast.makeText(getApplicationContext(), "Incorrect Information", Toast.LENGTH_SHORT).show();
        }
    }
}

Solution

  • In my comment I said:

    Using noHistory is not a solution, it just happens to work in most cases. When your app is no longer in the foreground, for reasons like an incoming call is received, you don't want your user experience to be lost, you want it to be paused. Correct management of onPause and onResume is a basic fundamental requirement, that using noHistory will temporarily avoid for you.

    Here is an example of how you manage the recognizer object when your app is paused and subsequently resumed.

    @Override
    protected void onResume() {
        super.onResume();
    
        if (recognizer == null) {
            // Set up recognizer again
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
    
        if (recognizer != null) {
            recognizer.cancel();
            recognizer.shutdown();
            recognizer = null;
        }
    }
    

    Whilst destroying your app every time it is paused, will resolve the solution of releasing the microphone and recognizer resource, this is not what you want to do - you want to gracefully manage the lifecycle of your application going into the background and coming to the foreground again, without the resources you are holding on to (such as the mic) causing an issue for other applications.