0

I am building an app that reads SMS's by converting them to speech. After installing the apk it runs but when I close it and try to open it again it crashes. It gives an error message saying the app keeps closing. When I set it to only read after a button click it was working fine but after I changed it to read on initialization this problem came about. Please help.

package com.example.receiver3;

import android.Manifest;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.speech.tts.TextToSpeech;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Button;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {
   ListView listView;
   Button btConvert, btNext;

   private static final int PERMISSION_REQUEST_READ_CONTACTS = 100;
   ArrayList<String> smsList;
   TextToSpeech textToSpeech;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       listView = (ListView) findViewById(R.id.idList);

       int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS);

        if (permissionCheck == PackageManager.PERMISSION_GRANTED){
        initializeTextToSpeech();     //edit
        showContact();
       }
       else{
           ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, PERMISSION_REQUEST_READ_CONTACTS);
      }
       btConvert = findViewById(R.id.bt_stop);
       btNext = findViewById(R.id.bt_next);


       btConvert.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               //get list value
               for (String s : smsList) {
                   textToSpeech.stop();
               }
           }
       });
      Button button = (Button) findViewById(R.id.bt_next);
      button.setOnClickListener(new View.OnClickListener() {
         @Override
          public void onClick(View v) {
           openActivity();
       }
   });
   }
    private void initializeTextToSpeech() {       //edit
    textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
        @Override
        public void onInit(int i) {
            if (i == TextToSpeech.SUCCESS){
                int lang = textToSpeech.setLanguage(Locale.UK);
            }
        }
    });
   }
   public void openActivity(){
      Intent intent = new Intent(this, Main2Activity.class);
      startActivity(intent);
  }

  @Override
  public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_READ_CONTACTS){
        showContact();
        initializeTextToSpeech();  //edit
    }
    else {
        Toast.makeText(this, "Permission Required", Toast.LENGTH_SHORT).show();
    }
}

private void showContact() {
    Uri inboxUri = Uri.parse("content://sms/inbox");
    smsList = new ArrayList<>();
    ContentResolver contentResolver = getContentResolver();

    Cursor cursor = contentResolver.query(inboxUri,null, null, null, null);
    while (cursor.moveToNext()){
        String number = cursor.getString(cursor.getColumnIndexOrThrow("address")).toString();
        String body = cursor.getString(cursor.getColumnIndexOrThrow("body")).toString();
        smsList.add("Number: "+number+ "\n" + "Body: "+body);
    }
    for (String s : smsList) {
        textToSpeech.speak(s,TextToSpeech.QUEUE_ADD, null);
    }
    cursor.close();
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,smsList);
    listView.setAdapter(adapter);


  }
 public void speak() {       //edit
    for (String s : smsList) {
        textToSpeech.speak(s,TextToSpeech.QUEUE_ADD, null);
    }
}

@Override
protected void onPause() {      //edit
    super.onPause();
    textToSpeech.stop();
}

@Override
protected void onStop() {
    super.onStop();
    textToSpeech.stop();

}


@Override
protected void onResume() {     //edit
    super.onResume();
    showContact();
    initializeTextToSpeech();
} 
}

1 Answers1

1

You are now calling showContact() which in turn calls textToSpeech.speak() before initialising the textToSpeech object with textToSpeech = new TextToSpeech().

The code works fine on the first run because the user hasn't given the requested permission yet and showContact() doesn't get called.

You need to change your onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    listView = (ListView) findViewById(R.id.idList);

    int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS);

    // *** MOVE THIS UP HERE. ***
    textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
        @Override
        public void onInit(int i) {
            if (i == TextToSpeech.SUCCESS){
                int lang = textToSpeech.setLanguage(Locale.UK);
                return;
            }
        }
    });

    if (permissionCheck == PackageManager.PERMISSION_GRANTED){
        showContact();
        return;
    } else {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, PERMISSION_REQUEST_READ_CONTACTS);
    }
    btConvert = findViewById(R.id.bt_convert);
    btNext = findViewById(R.id.bt_next);

    btConvert.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //get list value
            for (String s : smsList) {
                textToSpeech.stop();
            }
        }
    });
    Button button = (Button) findViewById(R.id.bt_next);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
            public void onClick(View v) {
            openActivity();
        }
    });
}

But that alone won't be enough as TTS isn't ready immediately. It's usable only after the onInit() callback has run and you receive the TextToSpeech.SUCCESS state there.

So, if you want to speak something as soon as possible at application startup then it needs to be triggered from onInit().

Maybe you can think of something like:

int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS);
if (permissionCheck != PackageManager.PERMISSION_GRANTED){
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, PERMISSION_REQUEST_READ_CONTACTS);
}

And then:

textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
    @Override
    public void onInit(int i) {
        if (i == TextToSpeech.SUCCESS){
            int lang = textToSpeech.setLanguage(Locale.UK);

            if (permissionCheck == PackageManager.PERMISSION_GRANTED){
                showContact();
            }
        }
    }
});

I don't see why would you have the return; calls at all. And actually in the original code the return; exits the onCreate() before even trying to initialize the TTS if the permission is already given.

Rahul Gaur
  • 1,661
  • 1
  • 13
  • 29
Markus Kauppinen
  • 3,025
  • 4
  • 20
  • 30
  • So I took your advice and made some changes in the code, though some suggested changes were already there. I couldn't figure out how to add the edited code block in the comments so I just edited the first initial code I posted before, you can check it out again. The thing is, with the new changes now the app crashes when I try to run it the first time, but it can reopen and run at any time but can only read after returning from another page which means it only works on resume if I'm not mistaken. The other thing is it only stops reading when it completes all the messages. – Timothy Mwansa Feb 19 '20 at 16:00
  • When I remove `showContact();` in the `onResume()` method it does not crash on first run and can read upon openning but only on the first run. after it only displays the messages. Please help – Timothy Mwansa Feb 19 '20 at 16:10