6

I am trying to create a button that allows me to record audio through a service, i want the button to have text: "Start Recording". On the OnClick event, i want the button text to change to: "Stop Recording".

I had this code working when it was in a class but now it is not working in a service, however, as I want the audio record to work as a service, I cannot seem to get the button's text to change. I am pretty new to coding so any help will be greatly appreciated!

My code is as follows:

Class:

public class Test extends AppCompatActivity {       

    public void Record(View view) {
        Intent intent = new Intent(this, RecordService.class);
        startService(intent);
    }    

    public void Play(View view) {
        Intent intent = new Intent(this, RecordService.class);
        stopService(intent);    
    }
}

Service:

import android.app.Service;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.widget.Button;
import android.view.View;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class RecordService extends Service {

    MediaRecorder mRecorder;
    public static String audioFilePath;
    public boolean isRecording = false;

    public RecordService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public void onCreate () {

        if(mRecorder == null){

            SimpleDateFormat s = new SimpleDateFormat("ddMMyyyy_hhmmss");
            String format = s.format(new Date());

            audioFilePath = Environment.getExternalStorageDirectory().
                    getAbsolutePath() +  "/" + format + ".3gpp";

            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mRecorder.setOutputFile(audioFilePath);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        }

        if (isRecording) {
            try{
                stopRecording();
                isRecording = false;
                ((Button)view).setText("Start Recording");
            }catch(Exception e){
                e.printStackTrace();
            }
        } else {
            try{
                startRecording();
                isRecording = true;
                ((Button)view).setText("Stop Recording");
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    public void startRecording() throws IllegalStateException, IOException{
        mRecorder.prepare();
        mRecorder.start();
    }

    public void stopRecording() throws IllegalStateException, IOException{
        mRecorder.stop();
        mRecorder.release();
    }


    public void onStartCommand()
    {
        SimpleDateFormat s = new SimpleDateFormat("ddMMyyyy_hhmmss");
        String format = s.format(new Date());

        audioFilePath = Environment.getExternalStorageDirectory().
                getAbsolutePath() +  "/" + format + ".3gpp";

        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(audioFilePath);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        try {
            mRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mRecorder.start();

    }

    public void onDestroy()
    {
       super.onDestroy();
        mRecorder.stop();
        mRecorder.release();

    }


}

Activity:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="kyr.com.knowyourrights.Test">

    <TextView android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:text="Record"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/RecordButton"
        android:layout_alignParentTop="true"
        android:onClick="Record">
    </Button>

</RelativeLayout>
Aleksandar G
  • 1,163
  • 2
  • 20
  • 25
Viverson
  • 85
  • 10

6 Answers6

1

You want to call button in service, try to use BroadcastReceiver

  • How do I link this within my existing code? where would I write the runOnUiThread method, as I cannot place it within my Boolean and also i cant create a new method with it? @hoanngothanh – Viverson May 12 '16 at 11:43
  • @Viverson like Xema comment, try to use otto library – hoanngothanh May 13 '16 at 06:06
1

This, indeed, can't work here since your ((Button) view) isn't instantiate in your onCreate().

What I suggest is to call, from the service, a function inside the Activity that performs what you want to do (here, change the text). You can achieve this using a BroadcastReceiver.

To do that, you can do the following :

Inside your Activity :

@Override
public void onResume() {
  super.onResume();

  // Register mMessageReceiver to receive messages.
  LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
      new IntentFilter("change-text"));
}

// handler for received Intents for the "change-text" event 
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Extract data included in the Intent
    String message = intent.getStringExtra("message");
    button.setText(message); //Assuming you have instantiate properly your button inside your onCreate()
  }
};

@Override
protected void onPause() {
  // Unregister since the activity is not visible
  LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
  super.onPause();
} 

Inside your Service :

// Send an Intent with an action named "change-text". 
private void sendMessage(boolean startRecording) {
  Intent intent = new Intent("change-text");
  // add data
  if (startRecording) intent.putExtra("message", "Start Recording");
  else intent.putExtra("message","Stop Recording");
  LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
} 

To change the text button from your service, you will just have to call the sendMessage function with either true or false as parameter depending on what you want to do !

You can see the source of the code, which I adapted to your situation, here : http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html#ownreceiver_localbroadcastmanager

Xema
  • 1,796
  • 1
  • 19
  • 28
0

Something like this would work

if (isRecording) {
            try {
                stopRecording();
                isRecording = false;
                yourcontext.runOnUiThread(new Runnable() {
                    public void run() {
                        ((Button) view).setText("Start Recording");
                    }
                });


            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                startRecording();
                isRecording = true;
                yourcontext.runOnUiThread(new Runnable() {
                    public void run() {
                        ((Button) view).setText("Stop Recording");
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
apmartin1991
  • 3,064
  • 1
  • 23
  • 44
  • Im getting the same issues with this as I was before, I imagine because a service is not supposed to interact with the UI therefore I cant change the Button view – Viverson May 13 '16 at 02:54
0

It may be better to change the text in the activity, could fit more to the spirit of the service. You should take a look at this topic: Example: Communication between Activity and Service using Messaging

In my opinion, to create a handler within a Messenger is a very useful and efficient way to create a communication bridge between your service and the activity. In this way you would likely create state tags representing the current state in your Service, e.g in your case:

Message message = new Message();
message.setData(b); // bundle containing extra data if you need it
message.what = MSG_RECORD_STOP;
mActivity.send(message);

Then in your activity, once you've got the callback from your Messenger, you could simply do something like:

(TextView) mRecordButton =(TextView)rootView.findViewById(R.id.RecordButton);
mRecordButton.setText("Stop Recording");
Community
  • 1
  • 1
Amesys
  • 816
  • 2
  • 10
  • 19
0

Why do you want to change the button text in the service? Maybe this is what you actually need. public class Test extends AppCompatActivity {

public void Record(View view) {
    Intent intent = new Intent(this, RecordService.class);
    startService(intent);
    yourButton.setText("Stop Recording");
}



public void Play(View view) {
    Intent intent = new Intent(this, RecordService.class);
    stopService(intent);
    yourButton.setText("Start Recording");

}

}

Guliash
  • 163
  • 1
  • 8
  • Does this mean I need two buttons? as I imagine these are essentially two different on click events, how would I give one button in my xml file two on click events? @Guliash – Viverson May 12 '16 at 22:21
0

Declare your button as static in your activity e.g

public static Button recordButton;
public void onCreate(Bundle savedInstanceState) {
   ....
   recordButton =(Button) findViewById(R.id.RecordButton);
   ....
}

then do something like this in your service where you want

WeakReference<Button> recordButton = new WeakReference<Button>(YourActivity.recordButton);
recordButton.setText("Start Recording");
Masum
  • 4,879
  • 2
  • 23
  • 28
  • This is not a good solution, there are far better ways to handle this kind of condition than to make a view static. – JoxTraex May 13 '16 at 04:46