Handler not updating ActionBar title - android

I am writing code for a music player and everything seems to be working fine except the ActionBar. When a song is playing, a thread probes the service every second for a change in the song and if there is a change in the song, a handler object changes the UI elements.
final Handler uiHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
Bundle data = msg.getData();
song = data.getParcelable("song_info");
songText.setText(song.getSongTitle());
artistText.setText(song.getSongArtist());
seekBar.setMax(mService.getDuration()/1000); //All these work
assert getSupportActionBar() != null;
getSupportActionBar().setTitle(mService.getSongName()); //Not updating
super.handleMessage(msg);
}
};
What this does is when there is a message from the thread, it updates the UI. I can confirm that this works and is invoked when I need it to. All the text views and the seekbar get updated as required. However, the getSupportActionBar().setTitle(...) doesn't update the title bar.
The same method of setting action bar title works if I do it in other methods of the same class. Am I missing something here?
Thanks in advance!
Update:
Here's some more code that the Activity has, in case it helps strike something.
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MusicService.MusicBinder mBinder = (MusicService.MusicBinder)iBinder;
mService = mBinder.getService();
pauseBtn = (ImageButton)findViewById(R.id.pause);
seekBar.setMax(mService.getDuration()/1000);
CounterThread cThread = new CounterThread();
Thread counter = new Thread(cThread);
counter.start();
if(mService.isPaused()) {
pauseBtn.setImageResource(R.drawable.ic_play_arrow_white_36dp);
}
else {
pauseBtn.setImageResource(R.drawable.ic_pause_white_36dp);
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private class CounterThread implements Runnable {
#Override
public void run() {
while(true) {
try {
if(!mService.getSongName().equals(song.getSongTitle())) //This is invoked when there is a change in the song.{
Message msg = uiHandler.obtainMessage();
Bundle b = new Bundle();
b.putParcelable("song_info", mService.getSong());
msg.setData(b); //I receive this message in the handler
uiHandler.sendMessage(msg);
}
seekBar.setProgress(mService.getCurrPosn()/1000);
Thread.sleep(1000);
} catch (Exception e){
e.printStackTrace();
}
}
}
}
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song_playing);
song = getIntent().getParcelableExtra("song_info");
songText = (TextView) findViewById(R.id.songname);
artistText = (TextView) findViewById(R.id.artistname);
songText.setText(song.getSongTitle());
artistText.setText(song.getSongArtist());
header = getSupportActionBar();
seekBar = (SeekBar)findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
// Do nothing
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Do nothing
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
mService.seek(progress*1000);
}
});
assert header != null;
header.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
header.setCustomView(R.layout.head_layout);
titleText = (TextView) findViewById(R.id.titleText);
titleText.setText(song.getSongTitle());
}
/*
More operations
*/
public void next(View v) {
mService.nextSong();
pauseBtn.setImageResource(R.drawable.ic_pause_white_36dp);
assert header != null;
header.setTitle(mService.getSongName()); // This works fine
songText.setText(mService.getSongName());
artistText.setText(mService.getArtistName());
titleText.setText(mService.getSongName());
seekBar.setMax(mService.getDuration()/1000);
}
Don't mind the slight indentation offset.

Related

Seekbar is still not updating

I made these two method to update seekbar every 100ms:
public void updateSeekBar() {
handler.postDelayed(mUpdateTimeTask, 100);
}
private Runnable mUpdateTimeTask = new Runnable() {
#Override
public void run() {
mySeekBar.setMax(mySong.getDuration());
x = mySong.getCurrentPosition();
mySeekBar.setProgress(x);
handler.postDelayed(this, 100);
}
};
and put it inside my playMusic method:
public void playMusic() {
//just a test from intent.getExtra
if(test.equalsIgnoreCase("Jason Mraz")) {
mySong = MediaPlayer.create(MusicClass.this, jm[musicCounter]);
displaySong(jm);
songNumbers = jm.length;
}else if(test.equalsIgnoreCase("fob")) {
mySong = MediaPlayer.create(MusicClass.this, fob[musicCounter]);
displaySong(fob);
songNumbers = fob.length;
}else if(test.equalsIgnoreCase("ed")) {
mySong = MediaPlayer.create(MusicClass.this, ed[musicCounter]);
displaySong(ed);
songNumbers = ed.length;
}
//when the song is completed
mySong.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
nextSong();
}
});
//seekbar update
mySeekBar.setMax(mySong.getDuration());
mySeekBar.setProgress(0);
mySong.start();
updateSeekBar();
}
this is my onCreate method:
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.music);
artistName = (TextView)findViewById(R.id.artistName);
song = (TextView)findViewById(R.id.song);
musicCounter = 0;
ifPlaying = true;
isRandom = false;
random = (ImageButton) findViewById(R.id.random);
stop = (ImageButton)findViewById(R.id.stop);
myImageView = (ImageView)findViewById(R.id.myImageView);
dice = new Random();
mySeekBar = (SeekBar)findViewById(R.id.mySeekBar);
test = getIntent().getStringExtra("test");
if(test.equalsIgnoreCase("Jason Mraz")) {
artistName.setText("Jason Mraz");
displayPP();
songNumbers = jm.length;
myImageView.setImageResource(R.drawable.jason_mraz);
} else if (test.equalsIgnoreCase("fob")) {
artistName.setText("Fall Out Boys");
displayPP();
songNumbers = fob.length;
myImageView.setImageResource(R.drawable.fall_out_boys);
} else if (test.equalsIgnoreCase("ed")) {
artistName.setText("Ed Sheeran");
displayPP();
songNumbers = ed.length;
myImageView.setImageResource(R.drawable.ed_sheeran);
}
playMusic();
mySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (b) {
mySong.seekTo(i);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
mySong.pause();
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (ifPlaying == true) {
mySong.start();
} else {
return;
}
}
});
}
My application says Unfortunately stopped. But when I remove the updateSeekbar in playMusic method it works fine, but without the updating seekBar every second. The setOnSeekBarChangeListener works perfectly fine, the only problem is I can't make updateSeekBar method work because it is alwats stopping my application and force exit.
The symptoms you are describing is called an ANR. It means that you is doing to much work in the main thread.
What you need todo is review what you are triggering in your mUpdateTimeTask.
Next make sure your handler is running on the UI since your are updating views:
Handler handler = new Handler(Looper.getMainLooper());

Can't pass an int from button if / else case to media player

how are you supposed to let the mediaplayer know what it is supposed to play?
wholeTextPlayer = MediaPlayer.create(Lesson1Reading.this, engAu);
It works fine if I declare the file in the top:
MediaPlayer wholeTextPlayer;
private int engAu = R.raw.l1r_en_l10;
Button btn_default_acc_whole;
It doesn't work from within a button click if / else statement wherever I try to put it with the following combination:
MediaPlayer wholeTextPlayer;
private int engAu;
Button btn_default_acc_whole;
The button click:
final Button btn_default_acc_whole = (Button) findViewById(R.id.btn_default_acc_whole);
btn_default_acc_whole.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
if (wholeTextPlayer.isPlaying()) {
wholeTextPlayer.pause();
} else {
wholeTextPlayer.start();
startPlayProgressUpdater();
}
setEngAu(R.raw.l1r_en_l10); //this line doesn't want to fit anywhere
}
});
The setter:
public void setEngAu(int engAu) {
this.engAu = engAu;
}
Of course they are separately placed in the activity, I just copied and pasted the relevant bits from it.
Thanks guys.
Here is the whole code:
'public class Lesson1Grammar extends Activity {
private SeekBar seekBar;
MediaPlayer wholeTextPlayer;
private int engAu;
private final Handler handler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.lesson1grammar);
WholeDefAccPlayer();
final RelativeLayout playerScreen = (RelativeLayout)findViewById(R.id.playerScreen);
final ImageButton btn_player_screen = (ImageButton) findViewById(R.id.btn_player_screen);
btn_player_screen.setOnClickListener(new View.OnClickListener() {
//this hides/unhides the part of the layout in which the player is
#Override
public void onClick(View arg0) {
if (playerScreen.isShown()) {
playerScreen.setVisibility(View.GONE);
} else {
playerScreen.setVisibility(View.VISIBLE);
}
}
});
final Button btn_default_acc_whole = (Button) findViewById(R.id.btn_default_acc_whole);
btn_default_acc_whole.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
if (wholeTextPlayer.isPlaying()) {
wholeTextPlayer.pause();
} else {
setEngAu(R.raw.default_acc_audio);
wholeTextPlayer.start();
startPlayProgressUpdater();
}
}
});
}
public void setEngAu(int engAu) {
this.engAu = engAu;
}
private void WholeDefAccPlayer() {
wholeTextPlayer = MediaPlayer.create(Lesson1Grammar.this, engAu);
((TextView) findViewById(R.id.getTitleOfAccent)).setText(R.string.btn_lesson1reading);
seekBar = (SeekBar) findViewById(R.id.seekBar);
seekBar.setMax(wholeTextPlayer.getDuration());
seekBar.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
seekChange(v);
return false;
}
});
}
public void startPlayProgressUpdater() {
seekBar.setProgress(wholeTextPlayer.getCurrentPosition());
if (wholeTextPlayer.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();
}
};
handler.postDelayed(notification, 1000);
}
else wholeTextPlayer.pause();
}
// This is event handler thumb moving event
private void seekChange(View v){
if(wholeTextPlayer.isPlaying()){
SeekBar sb = (SeekBar)v;
wholeTextPlayer.seekTo(sb.getProgress());
}
}
}
'
if your plan is to make the method setEngAu() is the controller as you mentioned in the comment. then you you need to use that method like this
public void setEngAu(int enAu)
{
this.EngAu = enAu;
wholeTextPlayer.setDataSource(engAu);
wholeTextPlayer.prepare();
}
you need to implement onPrepared listener from the media player
I do not know your classes but I assume where you say something like;
wholeTextPlayer = new MediaPlayer();
wholeTextPlayer.setOnPreparedListener(this);
here you start the player after it became prepared
public void onPrepared(MediaPlayer player)
{
player.start();
}
Remember to you will need to call wholeTextPlayer.release() if you do not want the player to hold on that resource anymore( think of it as memory issues - recommended if you check the documentation)
EDIT :
I adjusted your code a little, please take a look and let me know.
private SeekBar seekBar;
MediaPlayer wholeTextPlayer;
private int engAu;
final Handler handler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.lesson1grammar);
WholeDefAccPlayer();
final RelativeLayout playerScreen = (RelativeLayout) findViewById(R.id.playerScreen);
final ImageButton btn_player_screen = (ImageButton) findViewById(R.id.btn_player_screen);
btn_player_screen.setOnClickListener(new View.OnClickListener()
{
//this hides/unhides the part of the layout in which the player is
#Override
public void onClick(View arg0)
{
if(playerScreen.isShown())
{
playerScreen.setVisibility(View.GONE);
}
else
{
playerScreen.setVisibility(View.VISIBLE);
}
}
});
final Button btn_default_acc_whole = (Button) findViewById(R.id.btn_default_acc_whole);
btn_default_acc_whole.setOnClickListener(new View.OnClickListener()
{
public void onClick(View arg0)
{//problem here is
if(playerState == PlayerState_Playing)
{
wholeTextPlayer.pause();
setPlayerState(PlayerState_Paused);
}
else if(playerState == PlayerState_Paused)
{
wholeTextPlayer.start();
setPlayerState(PlayerState_Playing);
}
else
{
setEngAu(R.raw.default_acc_audio);
wholeTextPlayer.start();
startPlayProgressUpdater();
setPlayerState(PlayerState_Playing);
}
}
});
}
public void setEngAu(int engAu)
{
this.engAu = engAu;
if(wholeTextPlayer !=null)
{//just in case you call this method and player was not initialized yet
wholeTextPlayer.release();
}
setPlayerState(PlayerState_Preparing);
wholeTextPlayer = MediaPlayer.create(this, engAu);
}
private void WholeDefAccPlayer()
{
//this line probably will fail because engAu is not really initialized yet, unless you have default value for it
wholeTextPlayer = MediaPlayer.create(this, engAu);
((TextView) findViewById(R.id.getTitleOfAccent)).setText(R.string.btn_lesson1reading);
seekBar = (SeekBar) findViewById(R.id.seekBar);
//you can not call getDuration unless the player is prepared, so this might crash you
// seekBar.setMax(wholeTextPlayer.getDuration());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b)
{
if(playerState != PlayerState_Preparing)
{//if player state is preparing it means we can not seek yet, player.start() must be called first
wholeTextPlayer.seekTo(i);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
#Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
});
}
public void startPlayProgressUpdater()
{
if(seekBar.getMax() != wholeTextPlayer.getDuration())
{//in case you change track later, this will check if the seek bar max value with track duration.
// if they are not the same, then it will adjust it for you
seekBar.setMax(wholeTextPlayer.getDuration());
}
seekBar.setProgress(wholeTextPlayer.getCurrentPosition());
if(wholeTextPlayer.isPlaying())
{
Runnable notification = new Runnable()
{
public void run()
{
startPlayProgressUpdater();
}
};
handler.postDelayed(notification, 1000);
}
else wholeTextPlayer.pause();
}
// This is event handler thumb moving event
private void seekChange(View v)
{
if(wholeTextPlayer.isPlaying())
{
SeekBar sb = (SeekBar) v;
wholeTextPlayer.seekTo(sb.getProgress());
}
}
private final int PlayerState_Preparing = 0;
private final int PlayerState_Playing = 1;
private final int PlayerState_Paused = 2;
private int playerState;
private void setPlayerState(int state)
{//this is used to track the player state, because wholeTextPlayer.isPlaying() can return false in many conditions ( too general )
//you can check in the media player documentation to know more details about this
playerState = state;
}

Android: SeekBar is not updated using a Handler on 4.1.2 Device

friends I am developing an application which is playing small mp3 soundtracks (podcasts). Now my code which I am showing below is working on Android 3.2 as it should. But on Android 4.1.2 the SeekBar is not being updated by the handler instance. I have read about a bug in the SeekBar causing this issue but it should be fixed since Android 3.2. Any help or advice would be appreciated. Here is my code:
public class PodcastDetailFragment extends BaseFragment {
private MediaPlayer mediaPlayer = null;
private Button playPauseButton = null;
private SeekBar seekBar = null;
private TextView timeView = null;
private int totalDuration = 0;
private Bundle data;
private Handler handler=new Handler();
private Runnable runnable = new Runnable() {
public void run() {
int progress = mediaPlayer.getCurrentPosition();
seekBar.setProgress(progress);
int hours = ((progress/1000)/60)/60;
int minutes = ((progress/1000)/60)%60;
int seconds = ((progress/1000)%60)%60;
String hoursString = (hours<10)?"0":"";
String minutesString = (minutes<10)?"0":"";
String secondsString = (seconds<10)?"0":"";
timeView.setText(hoursString+hours+":"+minutesString+minutes+":"+secondsString+seconds);
handler.postDelayed(this, 100);
}
};
public static PodcastDetailFragment newInstance(Bundle bundle) {
PodcastDetailFragment f = new PodcastDetailFragment();
f.setArguments(bundle);
return f;
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
data = getArguments();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
getView().findViewById(R.id.preparing_track_progress_bar).setVisibility(View.VISIBLE);
super.onActivityCreated(savedInstanceState);
}
#Override
public void onPause() {
super.onPause();
if (mediaPlayer != null) {
LogUtils.d("Podcast MediaPlayer", "stop() inside the onPause() was called");
mediaPlayer.stop();
}
}
#Override
public void onDestroy() {
LogUtils.d("Podcast MediaPlayer", "release() was called");
mediaPlayer.release();
super.onDestroy();
}
/**
* The Fragment's UI is just a simple text view showing its instance number.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.podcasts_details, container, false);
TextView dv = (TextView) v.findViewById(R.id.durationView);
dv.setText(data.getString("duration"));
timeView = (TextView) v.findViewById(R.id.timeView);
timeView.setText("00:00:00");
playPauseButton = (Button) v.findViewById(R.id.playPauseButtonView);
playPauseButton.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
playPauseButton.setText(getResources().getString(
R.string.play));
} else {
mediaPlayer.start();
playPauseButton.setText(getResources().getString(
R.string.pause));
}
}
});
seekBar = (SeekBar) v.findViewById(R.id.seekBarView);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onStopTrackingTouch(SeekBar seekBar) {
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if (fromUser) {
mediaPlayer.seekTo(progress);
}
}
});
mediaPlayer =new MediaPlayer();
mediaPlayer
.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
totalDuration = mp.getDuration();
seekBar.setMax(totalDuration);
playPauseButton.setEnabled(true);
try{
getView().findViewById(R.id.preparing_track_progress_bar).setVisibility(View.INVISIBLE);
}catch(Exception ex){
ex.printStackTrace();
}
handler.postDelayed(runnable, 100);
}
});
mediaPlayer.setDataSource(Uri.parse(data.getString("podcast_url")).toString());
playPauseButton.setEnabled(false);
mediaPlayer.prepareAsync();
return v;
}
}
You should either use an AsyncTask or a Service (in case the music will play in background) instead of a Worker Thread with a Handler. The AsyncTask is extremely useful with it's onProgressUpdate method, you can use it to update the Bar based on the progress of the audio file.
Here's a basic example of an AsyncTask and how you should use it:
private class TestAsyncTask extends AsyncTask<Void, Integer, Void> {
#Override
protected Void doInBackground(Void... arg0) {
for(int i=0; i<songDuration; i++){
doSomething();
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
//UPDATE BAR
}
#Override
protected void onPostExecute(Void result) {
//DO SOMETHING WHEN FINISHED PLAYING
}
}
Anyway, here's the official doc: http://developer.android.com/reference/android/os/AsyncTask.html
I have solved this problem. The SeekBar was ok. It was just the issue that the lenght of a track is only given when you download the whole track. So to show the progress on a SeekBar properly you need to know the length of a track. So you take the length of a track and then call seekBar.setMax(lenghtOfTrack) and the progress is updated as it should by the method setProgress(progress).

How should I reuse the AnymoteClient service, so that I do not have to re-pair with google tv when I launch a new activity?

I am doing some investigations with the GoogleTV and a Android tablet. I have managed to make an android application that can send control messages to the google tv from the main Activity, what I am trying to do is launch a new activity from the main Activity and continue using the AnymoteClientService Service with the new activity. In my main activity I get an anymoteSender handle which I use to send KeyEvent messages to the google tv, how do I transfer this to the new activity (SlidepuzzleActivity)? I could instantiate it all again, but that would mean having to go through the whole pairing process again.
From the code below you will see that I have an anymoteSender in my SlidepuzzleActivity class, this will throw an error, but illustrates where I need to reuse that variable.
Code:
MainActivity.java:
package uk.co.myapp.gtvremote;
//imports removed for paste
public class MainActivity extends Activity implements ClientListener{
private AnymoteSender anymoteSender;
private TextView statusText;
protected AnymoteClientService mAnymoteClientService;
private static String statusPrefix = "Status: ";
private Context mContext;
private ProgressBar progressBar;
private Handler handler;
private TouchHandler touchPadHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progressBar = (ProgressBar) findViewById(R.id.a_progressbar);
progressBar.setVisibility(View.VISIBLE);
mContext = this;
ImageButton upArrowButton = (ImageButton) findViewById(R.id.upArrow);
ImageButton leftArrowButton = (ImageButton) findViewById(R.id.leftArrow);
ImageButton centreButton = (ImageButton) findViewById(R.id.centreButton);
ImageButton rightArrowButton = (ImageButton) findViewById(R.id.rightArrow);
ImageButton downArrowButton = (ImageButton) findViewById(R.id.downArrow);
ImageButton backButton = (ImageButton) findViewById(R.id.backButton);
ImageButton homeButton = (ImageButton) findViewById(R.id.homeButton);
Button testButton = (Button) findViewById(R.id.testButton);
upArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_UP);
}
});
leftArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT);
}
});
centreButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER);
}
});
rightArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT);
}
});
downArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN);
}
});
backButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_BACK);
}
});
homeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_HOME);
}
});
testButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent myIntent = new Intent(MainActivity.this, SlidepuzzleActivity.class);
MainActivity.this.startActivity(myIntent);
}
});
handler = new Handler();
// Bind to the AnymoteClientService
Intent intent = new Intent(mContext, AnymoteClientService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
statusText = (TextView) findViewById(R.id.statusText);
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
/*
* ServiceConnection listener methods.
*/
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAnymoteClientService = ((AnymoteClientService.AnymoteClientServiceBinder) service)
.getService();
mAnymoteClientService.attachClientListener(MainActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mAnymoteClientService.detachClientListener(MainActivity.this);
mAnymoteClientService = null;
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onConnected(AnymoteSender anymoteSender) {
if (anymoteSender != null) {
// Send events to Google TV using anymoteSender.
// save handle to the anymoteSender instance.
this.anymoteSender = anymoteSender;
attachTouchListnertoTouchPad();
} else {
statusText.setText(statusPrefix + "Connection attempt failed, cant find send handler");
//attempt to connect again?
//attemptToConnect();
}
// Hide the progressBar once connection to Google TV is established. Make the text display appropriately
handler.post(new Runnable() {
public void run() {
progressBar.setVisibility(View.INVISIBLE);
statusText.setText(statusPrefix + "Connected to GoogleTV");
}
});
}
#Override
public void onDisconnected() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Disconnected");
// Try to connect again if needed. This may be need to be done via button
attemptToConnect();
this.anymoteSender = null;
}
#Override
public void onConnectionError() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Connection error encountered");
// Try to connect again if needed.
attemptToConnect();
this.anymoteSender = null;
}
#Override
protected void onDestroy() {
if (mAnymoteClientService != null) {
mAnymoteClientService.detachClientListener(this);
}
unbindService(mConnection);
super.onDestroy();
}
private void attachTouchListnertoTouchPad()
{
// Attach touch handler to the touchpad view
touchPadHandler = new TouchHandler(
findViewById(R.id.touchPad), Mode.POINTER_MULTITOUCH, anymoteSender);
}
public void attemptToConnect()
{
//stub to invoke connection attempt
}
private void sendKeyEvent(final int keyEvent) {
// create new Thread to avoid network operations on UI Thread
if (anymoteSender == null) {
Toast.makeText(MainActivity.this, "Waiting for connection",
Toast.LENGTH_LONG).show();
return;
}
anymoteSender.sendKeyPress(keyEvent);
}
}
SlidepuzzleActivity.java:
package uk.co.myapp.gtvremote;
//imports removed for paste
public class SlidepuzzleActivity extends Activity implements ClientListener{
private AnymoteClientService mAnymoteClientService;
private Context mContext;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.slidepuzzle);
mContext = this;
ImageButton piece1x1 = (ImageButton) findViewById(R.id.piece1x1);
ImageButton piece1x2 = (ImageButton) findViewById(R.id.piece1x2);
ImageButton piece1x3 = (ImageButton) findViewById(R.id.piece1x3);
ImageButton piece2x1 = (ImageButton) findViewById(R.id.piece2x1);
ImageButton piece2x2 = (ImageButton) findViewById(R.id.piece2x2);
ImageButton piece2x3 = (ImageButton) findViewById(R.id.piece2x3);
ImageButton piece3x1 = (ImageButton) findViewById(R.id.piece3x1);
ImageButton piece3x2 = (ImageButton) findViewById(R.id.piece3x2);
ImageButton piece3x3 = (ImageButton) findViewById(R.id.piece3x3);
Intent intent = new Intent(mContext, AnymoteClientService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
piece1x1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
private ServiceConnection mConnection = new ServiceConnection() {
/*
* ServiceConnection listener methods.
*/
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAnymoteClientService = ((AnymoteClientService.AnymoteClientServiceBinder) service)
.getService();
mAnymoteClientService.attachClientListener(SlidepuzzleActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mAnymoteClientService.detachClientListener(SlidepuzzleActivity.this);
mAnymoteClientService = null;
}
};
private void sendKeyEvent(final int keyEvent) {
// create new Thread to avoid network operations on UI Thread
if (anymoteSender == null) {
Toast.makeText(SlidepuzzleActivity.this, "Waiting for connection",
Toast.LENGTH_LONG).show();
return;
}
anymoteSender.sendKeyPress(keyEvent);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.slidepuzzle, menu);
return true;
}
#Override
public void onConnected(AnymoteSender anymoteSender) {
// TODO Auto-generated method stub
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
#Override
public void onConnectionError() {
// TODO Auto-generated method stub
}
#Override
protected void onDestroy() {
if (mAnymoteClientService != null) {
mAnymoteClientService.detachClientListener(this);
}
unbindService(mConnection);
super.onDestroy();
}
}
Just published an update to AnymoteLibrary for this.
In your MainActivity call both bindService() (already) and startService() for AnymoteClientService. The reason behind calling startService() is to keep the service and its anymoteSender instance around so that other Activitys in the same app can use it.
In the second Activity, implement ClientListener ( if you want to get onDisconnected() callback) and bind to the service and attachClientListener(). To get the AnymoteSender instance, call AnymoteClientService.getAnymoteSender(). Note that it can return null if the connection to Google TV is lost.
When all Activitys are done using AnymoteSender, remember to call stopService() for AnymoteClientService.

Update UI from an event with a thread

Im working on a small application to try out an idea that I have. The idea is to periodically update the UI when event of some sort occurs. In the demo I've created, I'm updating a ProgressDialog every 2 seconds for 15 turns.
The problem I am having, which I don't quite understand is that when an event is handled, I send a message to the handler which is supposed to update the message in the ProgressDialog. When this happens however, I get an exception which states that I can't update the UI from that thread.
The following code appears in my Activity:
ProgressDialog diag;
String diagMessage = "Started loading...";
final static int MESSAGE_DATA_RECEIVED = 0;
final static int MESSAGE_RECEIVE_COMPLETED = 1;
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg){
diag.setMessage(diagMessage);
switch(msg.what){
case MESSAGE_DATA_RECEIVED:
break;
case MESSAGE_RECEIVE_COMPLETED:
dismissDialog();
killDialog();
break;
}
}
};
Boolean isRunning = false;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupDialog();
if(isRunning){
showDialog();
}
setContentView(R.layout.main);
}
void setupDialog(){
if(diag == null){
diag = new ProgressDialog(ThreadLoading.this);
diag.setMessage(diagMessage);
}
}
void showDialog(){
isRunning = true;
if(diag != null && !diag.isShowing()){
diag.show();
}
}
void dismissDialog(){
if(diag != null && diag.isShowing()){
diag.dismiss();
}
}
void killDialog(){
isRunning = false;
}
public void onStart(){
super.onStart();
showDialog();
Thread background = new Thread(new Runnable(){
public void run(){
try{
final ThreadRunner tr = new ThreadRunner();
tr.setOnDataReceivedListener(new ThreadRunner.OnDataReceivedListener(){
public void onDataReceived(String message){
diagMessage = message;
handler.handleMessage(handler.obtainMessage(MESSAGE_DATA_RECEIVED));
}
});
tr.setOnDataDownloadCompletedEventListener(new ThreadRunner.OnDataDownloadCompletedListener(){
public void onDataDownloadCompleted(String message){
diagMessage = message;
handler.handleMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
}
});
tr.runProcess();
}
catch(Throwable t){
throw new RuntimeException(t);
}
}
});
background.start();
}
#Override
public void onPause(){
super.onPause();
dismissDialog();
}
For curiosity sake, here's the code for the ThreadRunner class:
public interface OnDataReceivedListener {
public void onDataReceived(String message);
}
public interface OnDataDownloadCompletedListener {
public void onDataDownloadCompleted(String message);
}
private OnDataReceivedListener onDataReceivedEventListener;
private OnDataDownloadCompletedListener onDataDownloadCompletedEventListener;
int maxLoop = 15;
int loopCount = 0;
int sleepTime = 2000;
public void setOnDataReceivedListener(OnDataReceivedListener onDataReceivedListener){
this.onDataReceivedEventListener = onDataReceivedListener;
}
public void setOnDataDownloadCompletedEventListener(OnDataDownloadCompletedListener onDataDownloadCompletedListener){
this.onDataDownloadCompletedEventListener = onDataDownloadCompletedListener;
}
public void runProcess(){
for(loopCount = 0; loopCount < maxLoop; loopCount++){
try{
Thread.sleep(sleepTime);
onDataReceivedEventListener.onDataReceived(Integer.toString(loopCount));
}
catch(Throwable t){
throw new RuntimeException(t);
}
}
onDataDownloadCompletedEventListener.onDataDownloadCompleted("Download is completed");
}
Am I missing something? The logic makes sense to me and it looks like everything should work, I'm using a handler to update the UI like it is recommended.
Any help will be appreciated.
Thanks,
Tyrone
P.S. I'm developing for Android 1.5
I found the problem. After comparing my code with someone else's code which was very similar, the following small problem was found:
handler.handleMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
Should actually be:
handler.sendMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
Hopefully someone finds this useful and learns from my mistake :)
Regards,
Tyrone

Categories

Resources