package se.chalmers.pd.device; import se.chalmers.pd.device.ApplicationController.Callbacks; import se.chalmers.pd.device.NfcReader.NFCCallback; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import android.widget.ToggleButton; /** * This Class is separated from all logic and consist only of the view part of * the application. It sets up all the buttons and adds onclicklisteners. * Implements Callbacks to update the view and forward different actions. * * @author Patrik Thituson * */ public class MainActivity extends Activity implements Callbacks, View.OnClickListener, NFCCallback, DialogFactory.Callback { private ApplicationController controller; private TextView currentTrack, status; private ImageButton previous, play, next, pause; private ToggleButton start; private MenuItem connect, disconnect, install, uninstall; private SeekBar seekbar; private NfcReader nfcReader; private LoadingDialogFragment loadingDialog; private final String BROKER_URL = "tcp://192.168.43.147:1883"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); currentTrack = (TextView) findViewById(R.id.currenttrack); status = (TextView) findViewById(R.id.status); seekbar = (SeekBar) findViewById(R.id.seekbar); controller = new ApplicationController(this); setupButtons(); // log in to spotify controller.login(); nfcReader = new NfcReader(this); } /** * Sets up all the buttons for the view and adds onclick listeners. */ private void setupButtons() { start = (ToggleButton) findViewById(R.id.start); previous = (ImageButton) findViewById(R.id.prev); next = (ImageButton) findViewById(R.id.next); play = (ImageButton) findViewById(R.id.play); pause = (ImageButton) findViewById(R.id.pause); start.setOnClickListener(this); previous.setOnClickListener(this); next.setOnClickListener(this); play.setOnClickListener(this); pause.setOnClickListener(this); seekbar.setMax(300); seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { public void onStopTrackingTouch(SeekBar seekBar) { controller.seek((float) seekBar.getProgress() / seekBar.getMax()); } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } public void onStartTrackingTouch(SeekBar seekBar) { } }); } /** * Sets up the items in the menu * @param menu * @return */ @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); connect = (MenuItem) menu.findItem(R.id.connect); install = (MenuItem) menu.findItem(R.id.install); uninstall = (MenuItem) menu.findItem(R.id.uninstall); disconnect = (MenuItem) menu.findItem(R.id.disconnect); install.setEnabled(false); disconnect.setEnabled(false); uninstall.setEnabled(false); return true; } /** * Calls the controllers onDestroy to prevent crash in other threads */ @Override protected void onDestroy() { controller.onDestroy(); super.onDestroy(); } /** * Sets the text of currentTrack view * * @param text */ public void setText(String text) { currentTrack.setText(text); } /** * Helper method that makes the Player buttons visible called when user is * loged in. */ private void showPlayerButtons() { previous.setVisibility(View.VISIBLE); play.setVisibility(View.VISIBLE); next.setVisibility(View.VISIBLE); } /** * Callback that is called when the user has successfully logged in */ public void onPlayerLoggedIn() { showPlayerButtons(); } /** * Callback that is called when the Player has started playing */ public void onPlayerPlay() { runOnUiThread(new Runnable() { public void run() { play.setVisibility(View.GONE); pause.setVisibility(View.VISIBLE); setCurrentTrack(); } }); } /** * Callback that is called when the Player has paused playing */ public void onPlayerPause() { runOnUiThread(new Runnable() { public void run() { play.setVisibility(View.VISIBLE); pause.setVisibility(View.GONE); } }); } /** * Callback that is called when the Player has paused playing */ public void onPlayerNext() { runOnUiThread(new Runnable() { public void run() { setCurrentTrack(); } }); } /** * Callback that is called when the Web application has started successfully * or stopped. The parameter 'show' will decide whether to show or hide the * buttons */ public void onStartedApplication(final String status) { runOnUiThread(new Runnable() { public void run() { if(loadingDialog!=null){ loadingDialog.dismiss(); } String message = ""; switch (ActionResponse.valueOf(status)){ case success: uninstall.setEnabled(false); start.setChecked(true); message = getString(R.string.started_application); break; case error: start.setChecked(false); alert(getString(R.string.no_application_installed)); break; case pending: loadDialog(getString(R.string.starting_application)); break; default: uninstall.setEnabled(true); start.setChecked(false); message = getString(R.string.stopped_application); break; } changeStatus(message); setCurrentTrack(); } }); } /** * Launches a loading dialog with the specified message. * @param message */ private void loadDialog(String message){ loadingDialog = LoadingDialogFragment.newInstance(message); loadingDialog.show(getFragmentManager(), getString(R.string.loading_dialog)); } /** * Callback that is called when we want to change the visibility of the * buttons uninstall and install. Updates the status field for user feedback. */ public void onInstalledApplication(final boolean show) { runOnUiThread(new Runnable() { public void run() { if(loadingDialog!=null){ loadingDialog.dismiss(); } if (show) { uninstall.setEnabled(true); install.setEnabled(false); changeStatus(getString(R.string.installed_application_found)); } else { uninstall.setEnabled(false); install.setEnabled(true); changeStatus(getString(R.string.no_installed_application)); } } }); } /** * Callback that is called when the application has tried connecting with the broker. * launches a reconnect dialog if the connection was unsuccessful ß * @param connected true or false pending on the success of the connection */ public void onConnectedMQTT(final boolean connected) { runOnUiThread(new Runnable() { public void run() { if (connected) { connect.setEnabled(false); disconnect.setEnabled(true); changeStatus(getString(R.string.connected_to_broker)); } else { DialogFactory.buildConnectToUrlDialog(MainActivity.this, MainActivity.this, BROKER_URL, R.string.reconnect_dialog_message).show(); changeStatus(getString(R.string.could_not_connect_broker)); } } }); } /** * Callback that is called when the application has disconnected with the broker * on purpose. */ public void onDisconnectedMQTT(final boolean success) { runOnUiThread(new Runnable() { public void run() { if (success) { connect.setEnabled(true); disconnect.setEnabled(false); install.setEnabled(false); uninstall.setEnabled(false); changeStatus(getString(R.string.disconnected_from_broker)); } else { changeStatus(getString(R.string.could_not_disconenct_broker)); } } }); } /** * Callback from the connect/reconnect dialog that calls the controller with * the new broker url if the result is true * @param result * @param newBrokerUrl */ public void onConnectDialogAnswer(boolean result, String newBrokerUrl){ if(result) { controller.connect(newBrokerUrl); } } /** * Handles the menu items when they are selected to perform the correct action. * @param item * @return */ @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.install: loadDialog(getString(R.string.installing_application)); controller.createAndPublishSystemActions(Action.install); return true; case R.id.uninstall: controller.createAndPublishSystemActions(Action.uninstall); return true; case R.id.connect: DialogFactory.buildConnectToUrlDialog(this, this, BROKER_URL, R.string.connect_message).show(); return true; case R.id.disconnect: controller.disconnect(); return true; default: return super.onOptionsItemSelected(item); } } /** * The onclick method will check which button was clicked and forward this * to the controller. */ public void onClick(View v) { switch (v.getId()) { case R.id.start: Action action; if (start.isChecked()) { start.setChecked(false); action = Action.start; } else { action = Action.stop; } controller.createAndPublishSystemActions(action); break; case R.id.play: controller.createAndPublishPlayerActions(Action.play); break; case R.id.next: controller.createAndPublishPlayerActions(Action.next); break; case R.id.pause: controller.createAndPublishPlayerActions(Action.pause); break; case R.id.prev: controller.createAndPublishPlayerActions(Action.prev); break; } setCurrentTrack(); } /** * Updates the current track in the currentTrack view */ private void setCurrentTrack() { runOnUiThread(new Runnable() { public void run() { setText(controller.getCurrentTrack()); } }); } /** * Alerts the user when trying to perform an unavailable action * @param message */ private void alert(String message) { AlertDialog alert = new AlertDialog.Builder(this).setTitle(getString(R.string.warning)).setMessage(message) .setNeutralButton(getString(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // TODO maybe implement ? controller.connect(); dialog.cancel(); } }).create(); alert.show(); } /** * Updates the status view * @param message the message to show in the status view */ private void changeStatus(String message) { status.setText(message); } /** * Updates the seekbar * @param position */ public void onUpdateSeekbar(float position) { seekbar.setProgress((int) (position * seekbar.getMax())); } /** * Sets the current track */ public void onUpdatedPlaylist() { setCurrentTrack(); //this was implemented so this device could have a view of the playlist } /** * Callback from NFC that contains the url to the broker, forwards this url to * the controller. * @param url */ public void onNFCResult(String url) { controller.connect(url); } @Override protected void onPause() { super.onPause(); nfcReader.onPause(); } @Override protected void onResume() { super.onResume(); nfcReader.onResume(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); nfcReader.onNewIntent(intent); } }