Weird bug whilst making android app - java

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();
}
}

Related

Java Buffered Reader Stuttering when reading byte by byte

I just programmed a little Android app that needs to read from a program output and displays it in real time. After a lot of time I finally done a simple thing that reads from a Inputstream with a Buffered Reader that is being read byte by byte. But for somehow when there is a lot of stuff being displayed it hangs at a certain state and begins Stuttering a lot. So not the whole 100 lines are displayed but like 10 first and then all the other ones after one minute. Any way to fix this? I'm running Android Studio 3 Canary preview. Code for printing stdout to Textview is attached.
public void stdout(final InputStream i){
Runnable r = new Runnable() {
#Override
public void run() {
TextView m = (TextView) findViewById(R.id.textView3);
m.setMovementMethod(new ScrollingMovementMethod());
BufferedReader br = new BufferedReader(new InputStreamReader(i));
int line;
try{
while(true){
line = br.read();
if (line == -1)
{
break;
}
else
{
final StringBuilder b = new StringBuilder();
final int finalLine = line;
b.append(String.valueOf((char)finalLine));
final String t = b.toString();
m.post(new Runnable(){
#Override
public void run(){
TextView m = (TextView) findViewById(R.id.textView3);
m.append(t.toString());
}
});
}
}
}
catch (Exception e)
{
m.setText("error" + e.toString());
}
}
};
new Thread(r).start();
}

System.currentTimeMillis() behaving erroneously

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();

java xmlpull parse random string from XML

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?

How to save and load state of a GridView in Android

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)

Android TTS fails to speak large amount of text

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

Categories