I'm very new to Android programming and I'm trying to figure out how to get strings from a file and display those strings on buttons randomly.
The goal is to make a multiple choice test with randomly generated questions (based on a database) and keep track of the user's correct and incorrect answers (but I'm far from that part).
I watched an xmlpull tutorial which showed me how to pull a section of every line in the document and I tried to adapt it to pull a section of one line. Here's what I have:
String item;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_layout);
try {
item = getItemFromXML(this);
} catch (XmlPullParserException e) {
} catch (IOException e) {
}
String[] items = item.split("\n");
setListAdapter (new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items));
}
public String getItemFromXML(Test test) throws XmlPullParserException, IOException {
StringBuffer stringBuffer = new StringBuffer();
Resources res = test.getResources();
XmlResourceParser xpp = res.getXml(R.xml.database);
xpp.next();
int eventType = xpp.getEventType();
for (int sCount = 0; sCount<1; sCount++) {
if (eventType ==XmlPullParser.START_DOCUMENT)
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equals("character")) {
stringBuffer.append(xpp.getAttributeValue(null, "english") + "\n");
}
}
eventType = xpp.next();
}
return stringBuffer.toString();
}
The XML file looks kind of like this:
<root id="kanji">
<characters>
<character english="one" grade="1"/>
How can I make this work to pull a value from a random line?
Related
So, I've been stuck on this issue for a week where I launch my app and it freezes for almost 10 seconds on a white screen and after that the app gets really slow. I have a bottomnavigationview with a viewpager that loads four fragments, some of which have recyclerviews with custom adapters. Everytime I swipe or select a different tab, the app is painfully slow to select the new tab. Even the recyclerview scrolling is really slow. The weird part is that when data is switched off, the app works just fine and the viewpager swipes and recyclerview scrolls are fast enough.
I have seen several suggestions which suggest using gson parsing for the json data received from online, but the performance increase was negligible. I have also tried viewPager.setOffscreenPageLimit(4); but that hasn't helped. All my network calls have been placed on an asycntask and I have used StrictMode to confirm that. The app also works ok on an emulator, so the problem is only on real devices of all apis that I have tested.
//First Fragment
public void loadData(final Context context, final boolean b) {
HurlStack hurlStack = new HurlStack() {
#Override
protected HttpURLConnection createConnection(URL url) {
HttpsURLConnection httpsURLConnection = null;
try {
httpsURLConnection = CustomCAHttpsProvider.getHttpsUrlConnection(ServerConstants.LOAD_CHAT_URL, InfosylumApplication.getContext(), R.raw.certificate, false);
} catch (Exception e) {
e.printStackTrace();
Helper.showErrorDialog(e.getMessage(), e.toString(), InfosylumApplication.getContext());
}
return httpsURLConnection;
}
};
StringRequest stringRequest = new StringRequest(Request.Method.POST, ServerConstants.LOAD_CHAT_URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
mSwipeRefreshLayout.setRefreshing(false);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (isRunning) {
loadData(context, b);
}
}
}, 3000);
try {
JSONObject jsonObject = new JSONObject(response).getJSONObject("object");
TinyDB tinyDB = new TinyDB(context);
tinyDB.putString(TinyDBConstants.LOCAL_TYPING, "");
if (jsonObject.has("groupsAndInd")) {
final JSONArray array = jsonObject.getJSONArray("groupsAndInd");
LinkedHashMap<String, ChatNotification> chatNotificationLinkedHashMap = new LinkedHashMap<>();
final JSONArray localChatArray;
if (!tinyDB.getString(TinyDBConstants.LOCAL_CHAT_OTHER).isEmpty()) {
localChatArray = new JSONArray(tinyDB.getString(TinyDBConstants.LOCAL_CHAT_OTHER));
} else {
localChatArray = new JSONArray();
}
ArrayList<String> localChatIds = new ArrayList<>();
final ArrayList<String> onlineChatIds = new ArrayList<>();
String idsString = "";
for (int i = 0; i < localChatArray.length(); i++) {
JSONObject foodJson = localChatArray.getJSONObject(i);
localChatIds.add(foodJson.getString("id"));
idsString += foodJson.getString("id") + "\n";
}
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
List<String> urlList = Arrays.asList(object.getString("seenUsers").split(","));
if ((!urlList.contains(PatriceUser.getCurrentUser().getObjectId()) || object.getInt("messageStatus") < (Const.MESSAGE_STATUS_RECEIVED)) && !localChatIds.contains(object.getString("id")) && !object.getString("senderId").equals(PatriceUser.getCurrentUser().getObjectId())) {
String id = null, chatType = null;
switch (object.getString("chatType")) {
case Const.CHAT_TYPE_GROUP:
id = object.getString("groupId");
chatType = Const.CHAT_TYPE_GROUP;
break;
case Const.CHAT_TYPE_INDIVIDUAL:
id = object.getString(Const.SENDER_ID);
chatType = Const.CHAT_TYPE_INDIVIDUAL;
break;
}
boolean found = false;
if (chatNotificationLinkedHashMap.containsKey(id)) {
ChatNotification chatNotification = chatNotificationLinkedHashMap.get(id);
chatNotification.addChatObject(object);
chatNotificationLinkedHashMap.put(id, chatNotification);
} else {
ChatNotification chatNotification = new ChatNotification(context, chatType, id, object);
chatNotificationLinkedHashMap.put(id, chatNotification);
}
}
onlineChatIds.add(object.getString("id"));
}
tinyDB.putString(TinyDBConstants.LOCAL_CHAT_OTHER, array.toString());
for (Map.Entry<String, ChatNotification> entry : chatNotificationLinkedHashMap.entrySet()) {
String key = entry.getKey();
ChatNotification value = entry.getValue();
final InfosylumNotification infosylumNotification = new InfosylumNotification(context);
infosylumNotification.showNotification(value);
}
}
try {
if (jsonObject.has("groupsAndInd")) {
getActualMessages(jsonObject.getJSONArray("groupsAndInd"), jsonObject.getJSONArray("unread"));
}
if (jsonObject.has("notes")) {
loadNotes(jsonObject.getJSONArray("notes"));
}
} catch (NullPointerException ignore) {
Helper.showErrorDialog("ignore", ignore.getMessage(), getActivity());
}
} catch (JSONException e) {
} catch (NullPointerException e) {
//When the user has logged out
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("userId", PatriceUser.getCurrentUser().getObjectId());
params.put("loadGroupsQuery", loadGroupsQuery);
return params;
}
};
stringRequest.setRetryPolicy(new DefaultRetryPolicy(
40000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
VolleySingleton.getInstance(activity).addToRequestQueue(stringRequest);
stringRequest.setTag(REQUEST_TAG);
if (requestQueue == null) {
requestQueue = Volley.newRequestQueue(InfosylumApplication.getContext(), hurlStack);
}
requestQueue.add(stringRequest);
}
The other fragments are quite similar in how they obtain data there are also background jobs within the app and I think the hardest thing to do is to pinpoint which method is causing the bug. Also, using the profiler is out of the question because the device being used is of API 17. I'd just like to ask, is there any way I can pinpoint which method exactly is causing the sluggishness and the occasional ANRs that I receive?
Looking at your code you are creating new HttpURLConnection every time you call loadData(), which is unnecessary.Try to use Singleton design pattern to create httpConnection instance only once.
Here : new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (isRunning) {
loadData(context, b);
}
}
}, 3000);
this code you are executing from loadData() so there is recursive call (you call this method again and again every 3 seconds (if isRunning==true)).
In that case try to create all variable (specially lists) outside of the this method so you will just use them repeatedly not create new instance every time.
When you don't have an internet it is working fast because you aren't executing onResponse (which contains several loops) but only small onErrorResponse.
What are you trying to do with method? maybe I can help you by providing some alternative solution for it.
UPDATE where are you performing asynch tasks? Handler performs on the same thread it was created? is it AsynchTask or is it main thread are calling loadData()
So I'm trying to make my first ever Android app. It's just a simple tally counter for now but I've come across a weird bug.
You see, I've got a simple save and load function, an increment, decrement and reset button. and a TextView that displays the value all together (see below).
Now when I increment the value up to say 10 and close the app, it saves as it should and when I open the app again, it does come back as 10. However, when I then increment up to say 30 or 100, close and restart the value does not stick and comes out as either -1 or a value that is completely different.
What could be happening?
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
int value;
TextView textView_value;
final String filename = "tallyCountPlus";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadValue(filename);
textView_value = (TextView) findviewById(R.id.textView_value);
textView_value.setText(Integer.toString(value));
}
/** Called when the user presses the increment button */
public void incValue(View view) {
value++;
textView_value.setText(Integer.toString(value));
}
/** Called when the user presses the decrement button */
public void decValue(View view) {
if (value > 0) {
value--;
}
textView_value.setText(Integer.toString(value));
}
/** Called when the user presses the reset button */
public void resetValue(View view) {
value = 0;
textView_value.setText(Integer.setString(value));
}
#Override
public void onPause() {
super.onPause();
saveValue(filename, Context.MODE_PRIVATE);
}
#Override
public void onResume() {
super.onResume();
loadValue(filename);
textView_value.setText(Integer.toString(value));
}
public void onDestroy() {
super.onDestroy();
saveValue(filename, Context.MODE_PRIVATE);
}
private void loadValue(String name) {
try {
FileInputStream fis = openFileInput(name);
value = fis.read();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void saveValue(String name, int context) {
try {
FileOutputStream fos = openFileOutput(name, context);
fos.write(value);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Anyone know what my problem could be?
Thanks.
Jamie.
EDIT:
So I think I've fixed the bug by doing:
private void loadValue(String filename) {
/* this is wrapped in a try-catch statement */
FileInputStream fis = openFileInput(name);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
StringBuilder = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
value = Integer.parseInt(sb.toString());
fis.close() /* Not sure if this is actually neccessary? */
if (value == -1) {
value = 0;
}
}
private void saveValue(String name) {
/* This is also wrapped in a try-catch statement */
FileOutputStream fos = openFileOutput(name, Context.MODE_PRIVATE);
String data = Integer.toString(value);
fos.write(data.getBytes());
fos.close();
}
And after putting in a couple of values, it seems to work fine. However, I'm unsure as to whether it is appending or deleting the previous value and replacing it... I'd rather the latter to prevent the save file from getting too big.
onDestroy() is not dependable. Just stick with onPause(). Also, in general, for the methods that handle events when the activity is "going away", you should call the super method after your own code, to ensure your custom code can still execute.
Other things to check might be:
Is the method that saves the output replacing the previous file, or just appending to it?
Are you properly loading and saving each time?
If the file is properly loaded, is the value making it into the TextView? You might need to construct an anonymous Runnable that ensures that the TextView is updated by the main (UI) thread.
Sprinkle log statements liberally throughout the code to keep track of what the tally is, what you get out of the file, etc.
When diagnosing file issues, it helps to test on an emulator, which gives you root access to the device storage.
You need to modify loadValue() method. You are assigning fis.read() to value which returns next byte or -1 if EOF reached.
private void loadValue(String name) {
try {
FileInputStream in = openFileInput(name);
InputStreamReader inputStreamReader = new InputStreamReader(in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
value = Integer.parseInt(sb.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I'm creating a text file with timestamps derived from the result of System.currentTimeMillis(). My algorithm is such:
Create file and record timestamp of creation
Save timestamp each time a button is pressed
Subtract file-creation timestamp from button-press timestamp
Write result to the file
For some odd reason, the timestamp of the file creation is usually greater (younger, more recent) than the timestamp of the button presses, which always happen AFTER the file was created. This results in a negative value being returned from step 3.
What is causing this?
FileCreationMenu.java
public class FileCreationMenu extends Fragment {
public Button toggleRecordingButton;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.menu_fragment_calibrator, container, false);
toggleRecordingButton = (Button) v.findViewById(R.id.recordAudioToggle);
toggleRecordingButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recordAudio = !recordAudio; //toggle audio recording on/off
if (recordAudio==true){
AudioRecorder.createFile(System.currentTimeMillis()); //generate file for new track if record is togged ON
}
}
});
AudioRecorder.java
public static void createFile(long time) { //create file and record creation timeStamp
recordingStartTime = time;
myFile = new File(Environment.getExternalStorageDirectory(), "file.txt"); //save to external storage
try {
myFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void recordNote(long timeStamp){ //record timeStamps of notes
String playedNote = (timeStamp+ "\n" );
try {
fos = new FileOutputStream(myFile, true);
fos.write(playedNote.getBytes());
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void playTrack(String fileName){ //playback Notes
try {
FileInputStream fis = new FileInputStream(myFile.getPath());
BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
String line;
try {
int i =0;
while ((line = reader.readLine()) != null) { //read each line of input file
historicalTime[i] = Long.parseLong(line); //store time current line's note was played
if (i==0){
timeStream[i] = (Long.parseLong(line) - recordingStartTime); //if first note, calculate wait based on file creation time
}
else{
timeStream[i] = (Long.parseLong(line) - historicalTime[i-1]); //otherwise, calculate wait as amount of time note was played after most recent preceding note
}
i++;
}
} catch (IOException e){
e.printStackTrace();
}
for (int i=0; i<noteStream.length; i++){
try {
AudioRecorder.class.wait(timeStream[i]); //wait
//Play Note
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
MainActivity.java
...
public static void noteDetected(){
if (FileCreationMenu.recordAudio == true){
AudioRecorder.recordNote(System.currentTimeMillis());
}
Again, the problem is that recordingStartTime in AudioRecorder.java is often greater than the input parameter "timeStamp" to recordNote from the MainActivity. For example, in a recent debugging session recordingStartTime =1,450,573,093,044 while timeStamp=1,450,565,187,318.
What could be causing this seemingly impossible behavior?
The problem is this line:
myFile.createNewFile();
The JavaDoc for this method says
Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist
So every time you try to start a new recording you are actually reusing the same file (with timestamps from long ago) but record a new recordStartTime.
If you want to remove the old file first you will have to write
myFile.delete();
myFile.createNewFile();
I am making a simple game that uses GridView and a custom Adapter. It is basically a game that player can move through the GridView (simply changing the images of cells). The game has 10 levels. Problem is, when I get out of the activity (e.g. going back to the MainActivity) the game is reset. Also naturally when phone is turend off and on the game is reset.
I want to preserve the game state so when the player enters the GameActivity, he/she can continue with the game.
I only require 3 things to be saved, the Adapter, number of Level and the available moves. Simply if I knew how to work this out I could achieve what I need:
public class GameState implements Serializable {
private GameAssetAdapter mAdapter;
private int mLevel;
private int mAvailableMoves;
public GameState(GameAssetAdapter adapter, int level, int availableMoves) {
mAdapter = adapter;
mLevel = level;
mAvailableMoves = availableMoves;
}
public GameAssetAdapter getAdapter() {
return mAdapter;
}
public int getLevel() {
return mLevel;
}
public int getAvailableMoves() {
return mAvailableMoves;
}
}
So the question is, how can I save this object to internal storage and retrive it back when necessary?
I already have tried the onSaveInstanceState but it does not work as expected. phone off/on will reset this. Even if user wipes the app in the app list of android it will be reset. What should I do?
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(AppConstants.GAME_SAVE_ASSET_ADAPTER, mGameAssetAdapter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
if(savedInstanceState != null)
{ //Restore mGameAssetAdapter if was saved perviously
if(mGameAssetAdapter == null){
restoreGameAssetAdapter(savedInstanceState);
}
}
//TODO get level and states!!
mGameGridView = (GameGridView)findViewById(R.id.game_grid_view);
mGameGridView.setNumColumns(GameConstants.COLUMNS);
mGameGridView.setColumnWidth(GameHelper.getOptimumAssetImageWidth(this,
GameConstants.COLUMNS));
if(mGameAssetAdapter == null) {
mGameAssetAdapter = new GameAssetAdapter(this, mLevel);
}
mGameGridView.setAdapter(mGameAssetAdapter);
this.setTitle("Snakes and Ladders - Level " + mLevel);
setupEvents();
}
private void restoreGameAssetAdapter(Bundle savedInstanceState) {
if(savedInstanceState.getSerializable("GAME_ASSET_ADAPTER") != null){
mGameAssetAdapter =
(GameAssetAdapter) savedInstanceState.
getSerializable("GAME_ASSET_ADAPTER");
Log.v(TAG, "Restored saved GameAssetAdapter! Hoooray!");
}
}
You can just write the state to storage. Here is some code from my personal stash:
private static byte[] readBytes(String dir, Context context) throws IOException
{
FileInputStream fileIn = null;
DataInputStream in = null;
byte[] buffer = null;
fileIn = context.openFileInput(dir);
in = new DataInputStream(fileIn);
int length = in.readInt();
buffer = new byte[length];
for(int x = 0;x < buffer.length;x++)
buffer[x] = in.readByte();
try
{
fileIn.close();
} catch (Exception e) {}
try
{
in.close();
} catch (Exception e) {}
fileIn = null;
in = null;
return buffer;
}
private static void writeBytes(String dir, byte bytes[], Context context) throws IOException
{
FileOutputStream fileOut = null;
DataOutputStream out = null;
fileOut = context.openFileOutput(dir, Context.MODE_PRIVATE);
out = new DataOutputStream(fileOut);
int length = bytes.length;
out.writeInt(length);
out.write(bytes);
out.flush();
try
{
fileOut.close();
} catch (Exception e) {}
try
{
out.close();
} catch (Exception e) {}
}
You can save your state by using the writeBytes() method, then when the app is relaunched, all you have to do is use readBytes() to restore the game state.
If I may make one small structural suggestion, I like to make a 'package' class that holds my state variables when I write it to disk like this:
public class SavedState implements Serializible
{
public GameState state;
int id;
...
}
Then when you write this to disk, you will have all of your state variables in one clean class. I would also recommend not saving your DisplayAdapter for your list view (you might have a lot of problems) or whatever it is. Save the underlying data structure to this package class and then create a new DisplayAdapter when you resume the app state.
If you don't know how to turn an object into a byte array, here is the SO question for it:
Java: object to byte[] and byte[] to object converter (for Tokyo Cabinet)
I am trying to speak out large amount of text using Android Text To Speech. I using default Google speech engine. Below is my code.
public class Talk extends Activity implements TextToSpeech.OnInitListener {
private ImageView playBtn;
private EditText textField;
private TextToSpeech tts;
private boolean isSpeaking = false;
private String finalText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_talk);
//Intialize the instance variables
playBtn = (ImageView)findViewById(R.id.playBtn);
textField = (EditText)findViewById(R.id.textField);
//Resister the listeners
playBtn.setOnClickListener(new PlayBtnAction());
//Other things
tts = new TextToSpeech(this,this);
//Get the web page text if called from Share-Via
if (Intent.ACTION_SEND.equals(getIntent().getAction()))
{
new GetWebText().execute("");
}
}
//This class will execute the text from web pages
private class GetWebText extends AsyncTask<String,Void,String>
{
#Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
String websiteText = "";
try {
//Create a URL for the desired page
URL url = new URL(text);
// Read all the text returned by the server
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String str;
StringBuffer strBuffer = new StringBuffer("");
while ((str = in.readLine()) != null)
{
strBuffer.append(str+"\n"+"\n");
}
in.close();
String html = strBuffer.toString();
Document doc = Jsoup.parse(html);
websiteText = doc.body().text(); // "An example link"
//Toast.makeText(this, websiteText, Toast.LENGTH_LONG).show();
}
catch(Exception e)
{
Log.e("web_error", "Error in getting web text",e);
}
return websiteText;
}
#Override
protected void onPostExecute(String result)
{
textField.setText(result);
}
}
}
//Class to speak the text
private class PlayBtnAction implements OnClickListener
{
#Override
public void onClick(View v)
{
// TODO Auto-generated method stub
if(!isSpeaking)
{
isSpeaking = true;
//speak(textField.getText().toString());
finalText = textField.getText().toString();
new SpeakTheText().execute(finalText);
isSpeaking = false;
}
else
{
isSpeaking = false;
tts.stop();
}
}
}
#Override
public void onInit(int status) {
// TODO Auto-generated method stub
if(status==TextToSpeech.SUCCESS)
{
int result = tts.setLanguage(Locale.UK);
if(result==TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED)
{
Toast.makeText(this, "Language Not Supported", Toast.LENGTH_LONG).show();
}
}
}
//This class will speak the text
private class SpeakTheText extends AsyncTask<String,Void,String>
{
#Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
tts.speak(params[0], TextToSpeech.QUEUE_FLUSH, null);
return null;
}
}
#Override
public void onDestroy()
{
if(tts!=null)
{
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
}
But the issue here is, when there is a large chunk of text (lets say you have extracted text from a web page) the TTS fails to read it. If I remove most of the text, then it will read it. Why is this happening?
When I am about to read the large text, the LogCat however display something like this
10-11 07:26:05.566: D/dalvikvm(2638): GC_CONCURRENT freed 362K, 44% free 3597K/6312K, paused 17ms+8ms, total 93ms
The String length should not be longer than pre-defined length, from docs:
Parameters
text The string of text to be spoken. No longer than getMaxSpeechInputLength() characters.
Returned value by getMaxSpeechInputLength() may vary from device to device, but according to AOSP source that is whopping 4000:
/**
* Limit of length of input string passed to speak and synthesizeToFile.
*
* #see #speak
* #see #synthesizeToFile
*/
public static int getMaxSpeechInputLength() {
return 4000;
}
Try not to exceed that limit: compare input text length with that value and split into separate parts if necessary.
Use this code...Working for any file ..
just send the string to speech function..
private void speech(String charSequence) {
int position ;
int sizeOfChar= charSequence.length();
String testStri= charSequence.substring(position,sizeOfChar);
int next = 20;
int pos =0;
while(true) {
String temp="";
Log.e("in loop", "" + pos);
try {
temp = testStri.substring(pos, next);
HashMap<String, String> params = new HashMap<String, String>();
params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, temp);
engine.speak(temp, TextToSpeech.QUEUE_ADD, params);
pos = pos + 20;
next = next + 20;
} catch (Exception e) {
temp = testStri.substring(pos, testStri.length());
engine.speak(temp, TextToSpeech.QUEUE_ADD, null);
break;
}
}
}
In case someone might find this helpful. When you split the large text into strings, do not set the length of each string to the exact value of getMaxSpeechInputLength(). Subtract the string length by 1. Otherwise, only the last chunk of string could be read by TTS.
int length = toSpeech.getMaxSpeechInputLength() - 1;
Iterable<String> chunks = Splitter.fixedLength(length).split(largeText);
Lists.newArrayList(chunks);
It is worse than the 4000 characters limit in practice on Android. There are some TTS engines that limit the input length a lot more. For example Nuance.tts and vocalizer.tts engines won't speak any string longer than about 512 characters (from my tests some time ago). Today I hit a limit of below 300 characters in es.codefactory.eloquencetts package, which simply crashes if the string I send to it is more than 256-300 characters. I divide the contents into sentences, and guard for sentences longer than the above limit, further sub-dividing them in my app code.
Greg
If you follow ozbek's advice you should be fine. I too have large text files that I want spoken. I simply used the streamreader method and everything works fine. heres' PART of my code. it's the part that you should use. My code does a bit more than you want but it works for me and may work for you.
Dim sReader As StreamReader = New StreamReader(Story_file)
Try
Do Until EndOfStream '= True
Dim line_to_speak As String = sReader.ReadLine
Dim vc = Mid(line_to_speak, 1, 1) <- you dont need this
Select Case vc <- you dont need this
Case Is = "/" <- you dont need this
voice_index = Val(Mid(line_to_speak, 2, 2)) <- you dont need this
srate = Val(Mid(line_to_speak, 5, 2)) <- you dont need this
edassistv.lstVoices.SelectedIndex = voice_index <- you dont need this
selected_voice = edassistv.lstVoices.SelectedItem <- you dont need this
Case Else<- you dont need this
synth.SelectVoice(selected_voice)
synth.Speak(line_to_speak)
End Select<- you dont need this
Loop
Catch ex As Exception
GoTo finish