Viverson
Viverson

Reputation: 85

((Button)view) inside a service is not working

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>

Upvotes: 6

Views: 131

Answers (6)

hoanngothanh
hoanngothanh

Reputation: 90

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

Upvotes: 1

Masum
Masum

Reputation: 4959

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

Upvotes: 0

Amesys
Amesys

Reputation: 836

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

Upvotes: 0

Guliash
Guliash

Reputation: 163

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

}

}

Upvotes: 0

Anthony
Anthony

Reputation: 3074

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

Upvotes: 0

Xema
Xema

Reputation: 1886

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

Upvotes: 1

Related Questions