package org.example.mqtt;
import java.util.List;
import java.util.Vector;
import org.example.mqtt.MQTTSubscriberService.IncomingHandler;
import org.example.mqtt.model.NotifService;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.app.FragmentTransaction;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends FragmentActivity implements TabListener, IServiceChangeListener
, OnClickListener{
private final String TAG = "MQTT main activity";
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
static final int MSG_CONNECTED = 1;
static final int MSG_DISCONNECTED = 2;
static final int MSG_NEW_MESSAGE = 3;
static final int RECONNECT_TIMEOUT_ON_SERVER = 4; // the server does not let one to reconnect too fast
static final int SUBSCRIPTION_DONE = 5;
static final int UNSUBSCRIPTION_DONE = 6;
private Messenger mService = null;// handletToService
boolean isBound = false;
boolean is_phone;
List<Fragment> fragments;
private Button showAllServicesButton = null; // only available on tablet
// connect == true
// disconnect == false
private void setConnectButtons(boolean connected){
Button connectButton = (Button) findViewById(R.id.connectButton);
Button disconnectButton = (Button) findViewById(R.id.disconnectButton);
EditText serverUrl = (EditText) findViewById(R.id.url_value);
if(null != connectButton && null != disconnectButton && null != serverUrl){
connectButton.setEnabled(!connected);
disconnectButton.setEnabled(connected);
serverUrl.setEnabled(!connected);
}
}
private void toastAlert(String text){
Context context = getApplicationContext();
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
// TODO: potentially test if it is not behind a captive portal
// or if the port is blocked
public boolean isOnline() {
ConnectivityManager cm =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
}
return false;
}
/**
* Target we publish for clients to send messages to myself.
*/
final Messenger clientMessenger = new Messenger(new MainActMsgHandler());
/**
* Handler of incoming messages from clients
* As I am expecting to communicate only with the Main activity thread
* I will not get the replyTo from the message all the time to know who
* to answer to
*/
class MainActMsgHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CONNECTED:
setConnectButtons(true);
toastAlert("Connected");
// TODO: maybe add a toast
break;
case MSG_DISCONNECTED:
toastAlert("Disconnected");
setConnectButtons(false);
break;
case RECONNECT_TIMEOUT_ON_SERVER:
toastAlert("Failed to connect");
setConnectButtons(false);
break;
case SUBSCRIPTION_DONE:
// TODO: maybe add a toast
break;
case UNSUBSCRIPTION_DONE:
// TODO: maybe add a toast
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
isBound = true;
//textStatus.setText("Attached.");
Log.d(TAG, "connection created, got the handler to the service in the activy.");
// gonna send my handler to activity
try {
Message msg = Message.obtain(null,
MQTTSubscriberService.MSG_BIND);
msg.replyTo = clientMessenger;
mService.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "exception sending message to service - " + e);
}
// try to connect on startup
connect();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
isBound = false;
//textStatus.setText("Disconnected.");
Log.d(TAG, "lost the handler to the service in the activy.");
}
};
private void doBindService() {
Intent bindingIntent = new Intent(this, MQTTSubscriberService.class);
startService(bindingIntent);
bindService(bindingIntent, mConnection, Context.BIND_AUTO_CREATE);
Log.d(TAG, "connection to service being created.");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
is_phone= getResources().getBoolean(R.bool.is_phone);
if(is_phone){
// Phone Tabbed Initilization
viewPager = (ViewPager) findViewById(R.id.pager);
actionBar = getActionBar();
//List<Fragment>
// WATCH OUT! unfortunatelly dealing with fragments on a page adapter is a bit
// of a mess and I cant get a single reference to them and therefore must
// identified base on the order in which they are added to this vector
// therefore do not change this order before checking the code!
fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, StatusListFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ServicesListFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ConfigFragment.class.getName()));
mAdapter = new TabsPagerAdapter(getSupportFragmentManager(),fragments);
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (String tab_name : MqttApplication.tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));
}
/**
* on swiping the viewpager make respective tab selected
* */
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
// lock the screen in portrait mode
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}//end of phone OnCreate
else{
//tablet onCreate
StatusListFragment fragment = (StatusListFragment) Fragment.instantiate(this, StatusListFragment.class.getName());
android.support.v4.app.FragmentTransaction fft = getSupportFragmentManager().beginTransaction();
fft.add(R.id.notifFragment_container, fragment, "all_status_frag");
fft.commit();
showAllServicesButton = (Button) findViewById(R.id.showAllServicesButton);
showAllServicesButton.setOnClickListener(this);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}//end of tablet onCreate
Log.d(TAG, "going to bind to the service.");
doBindService();
}
@Override
public void onStart(){
super.onStart();
Log.d(TAG, "on start called, should we call the connect?");
}
public void connect(){
Log.d(TAG, "connect called on activity");
// check if there is internet connectivity
if(isOnline()== false){
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(this, "Connection failed. Please ensure you are connected to the internet", duration);
toast.show();
return;
}
sendMessageToMQTTservice(MQTTSubscriberService.MSG_CONNECT);
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
}
public void sendMessageToMQTTservice(int message){
// gonna send my handler to activity
try {
Message msg = Message.obtain(null,
message);
msg.replyTo = clientMessenger;
mService.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "exception sending message to service - " + e);
}
}
// method called when a NotifService has been added by the dialog fragment
@Override
public void notifyServiceListChanged(boolean added, String topic) {
MqttApplication app = (MqttApplication) getApplication();
if(app.isConnection()){// Ill just un/subscribe immediately if Im connected
Message msg;
if(added){
msg = Message.obtain(null,MQTTSubscriberService.MSG_SUBSCRIBE);
}else{
msg = Message.obtain(null,MQTTSubscriberService.MSG_UNSUBSCRIBE);
}
Bundle b = new Bundle();
b.putString("topic", topic);
msg.setData(b);
msg.replyTo = clientMessenger;
try{
mService.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "exception sending message to service - " + e);
}
}
// refresh the views
if(is_phone){
ServicesListFragment frag = (ServicesListFragment) mAdapter.findFragmentByPosition(1); // TODO: change this 1 for a static number
if(null != frag){
frag.notifyServiceListChange();
}
StatusListFragment frag2 = (StatusListFragment) mAdapter.findFragmentByPosition(0); // TODO: change this 0 for a static number
if(null != frag2){
frag2.notifyServiceChanged();
}
}
else{
ServicesListFragment frag = (ServicesListFragment) getSupportFragmentManager().findFragmentById(R.id.servicelist_fragment);
if(null != frag){
frag.notifyServiceListChange();
}
StatusListFragment frag2 = (StatusListFragment) getSupportFragmentManager().findFragmentByTag("all_status_frag");
if(null != frag2){
frag2.notifyServiceChanged();
}
showAllServices();
}
}
// onClick to capture the need to call the AddServiceDialog
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.addServiceButton:
// it was the addServiceButton
FragmentManager fm = getSupportFragmentManager();
AddServiceDialogFragment frag = new AddServiceDialogFragment();
frag.show(fm, AddServiceDialogFragment.class.getName());
break;
case R.id.showAllServicesButton:
showAllServices();
break;
}
}
public void showAllServices(){
// check if fragment is already there
StatusListFragment fragment = (StatusListFragment) getSupportFragmentManager().findFragmentByTag("all_status_frag");
if ( (null == fragment) || (false == fragment.isInLayout()) ) {
// as the fragment is not there, I'll reintroduce it
if(null == fragment){
fragment = (StatusListFragment) Fragment.instantiate(this, StatusListFragment.class.getName());
Log.d(TAG, "all notifications fragment is recreated");
}
android.support.v4.app.FragmentTransaction fft = getSupportFragmentManager().beginTransaction();
fft.replace(R.id.notifFragment_container, fragment, "all_status_frag");
fft.commit();
// and Ill unhighlight the selected service on the service list
ServicesListFragment servListFrag = (ServicesListFragment) getSupportFragmentManager().findFragmentById(R.id.servicelist_fragment);
if(null != servListFrag){
servListFrag.unhighlightCurrentSelectedRow();
}
}
}
// (the phone part is not yet working), in fact I should probably change
// the service specific list it to an activity itself
// this function can be called by fragments to invoke the creation and
// display of a fragment with the service specific notifications
public void showServiceSpecificNotifications(String serviceURI, String serviceName){
if(null == serviceName || serviceName.isEmpty()){
MqttApplication app = (MqttApplication) getApplication();
serviceName = app.getServiceNameFromURI(serviceURI);
}
if(is_phone){
// Create new fragment and transaction
Fragment newFragment = Fragment.instantiate(this, ServiceSpecifNotListFragment.class.getName());
Bundle bundle = new Bundle();
bundle.putString(MqttApplication.SERVICE_URI_BUNDLE_TAG, serviceURI);
bundle.putString(MqttApplication.SERVICE_NAME_BUNDLE_TAG, serviceName);
newFragment.setArguments(bundle);
android.support.v4.app.FragmentTransaction fft = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
// test 1
//Fragment oldServiceFragment = mAdapter.findFragmentByPosition(1);
//fft.replace(oldServiceFragment., newFragment, "specific_service_notif_list");
// test 2
//fft.add(R.id.service_notif_list_fragment, newFragment);
//fft.show(newFragment);
//fft.addToBackStack(null);
// Commit the transaction
fft.commit();
}
else{
Fragment newFragment = Fragment.instantiate(this, ServiceSpecifNotListFragment.class.getName());
Bundle bundle = new Bundle();
bundle.putString(MqttApplication.SERVICE_URI_BUNDLE_TAG, serviceURI);
bundle.putString(MqttApplication.SERVICE_NAME_BUNDLE_TAG, serviceName);
newFragment.setArguments(bundle);
android.support.v4.app.FragmentTransaction fft = getSupportFragmentManager().beginTransaction();
fft.replace(R.id.notifFragment_container, newFragment, "specific_service_notif_list");
fft.commit();
//highlight the service in the service fragment list
ServicesListFragment servListFrag = (ServicesListFragment) getSupportFragmentManager().findFragmentById(R.id.servicelist_fragment);
if(null != servListFrag){
servListFrag.highlightRowFromURI(serviceURI);
}
}
Log.d(TAG, "show service finished to be called");
}
// TODO: review this one
// This the important bit to make sure the back button works when you're nesting fragments. Very hacky, all it takes is some Google engineer to change that ViewPager view tag to break this in a future Android update.
@Override
public void onBackPressed() {
if(is_phone){
Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":"+viewPager.getCurrentItem());
if (fragment != null) // could be null if not instantiated yet
{
if (fragment.getView() != null) {
// Pop the backstack on the ChildManager if there is any. If not, close this activity as normal.
if (!fragment.getChildFragmentManager().popBackStackImmediate()) {
finish();
}
}
}
}
else{
super.onBackPressed();
}
}
/*
public void replaceFragmentToServiveListNot(String serviceURI) {
Log.d(TAG, "replace fragment called");
fragments.clear();
//fragments.removeAll(fragments);
fragments.add(Fragment.instantiate(this, StatusListFragment.class.getName()));
Fragment newFragment = new ServiceSpecifNotListFragment();
Bundle bundle = new Bundle();
bundle.putString(MqttApplication.SERVICE_URI_BUNDLE_TAG, serviceURI);
newFragment.setArguments(bundle);
fragments.add(newFragment);
fragments.add(Fragment.instantiate(this, ConfigFragment.class.getName()));
mAdapter.notifyDataSetChanged();
}*/
}