package org.droidplanner.android.activities.helpers; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.media.AudioManager; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.FragmentManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import com.o3dr.android.client.Drone; import com.o3dr.android.client.apis.CapabilityApi; import com.o3dr.android.client.apis.MissionApi; import com.o3dr.android.client.apis.VehicleApi; import com.o3dr.services.android.lib.drone.attribute.AttributeEvent; import com.o3dr.services.android.lib.drone.attribute.error.CommandExecutionError; import com.o3dr.services.android.lib.model.SimpleCommandListener; import org.droidplanner.android.AppService; import org.droidplanner.android.DroidPlannerApp; import org.droidplanner.android.R; import org.droidplanner.android.dialogs.SlideToUnlockDialog; import org.droidplanner.android.dialogs.SupportYesNoDialog; import org.droidplanner.android.dialogs.SupportYesNoWithPrefsDialog; import org.droidplanner.android.fragments.SettingsFragment; import org.droidplanner.android.fragments.actionbar.VehicleStatusFragment; import org.droidplanner.android.proxy.mission.MissionProxy; import org.droidplanner.android.utils.Utils; import org.droidplanner.android.utils.prefs.DroidPlannerPrefs; import org.droidplanner.android.utils.unit.UnitManager; import org.droidplanner.android.utils.unit.systems.UnitSystem; /** * Parent class for the app activity classes. */ public abstract class SuperUI extends AppCompatActivity implements DroidPlannerApp.ApiListener, SupportYesNoDialog.Listener, ServiceConnection { private static final String MISSION_UPLOAD_CHECK_DIALOG_TAG = "Mission Upload check."; private static final IntentFilter superIntentFilter = new IntentFilter(); static { superIntentFilter.addAction(AttributeEvent.STATE_CONNECTED); superIntentFilter.addAction(AttributeEvent.STATE_DISCONNECTED); superIntentFilter.addAction(SettingsFragment.ACTION_ADVANCED_MENU_UPDATED); } private final BroadcastReceiver superReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); switch (action) { case AttributeEvent.STATE_CONNECTED: onDroneConnected(); break; case AttributeEvent.STATE_DISCONNECTED: onDroneDisconnected(); break; case SettingsFragment.ACTION_ADVANCED_MENU_UPDATED: supportInvalidateOptionsMenu(); break; } } }; private ScreenOrientation screenOrientation = new ScreenOrientation(this); private LocalBroadcastManager lbm; /** * Handle to the app preferences. */ protected DroidPlannerPrefs mAppPrefs; protected UnitSystem unitSystem; protected DroidPlannerApp dpApp; private VehicleStatusFragment statusFragment; @Override public void setContentView(int resId){ super.setContentView(resId); final int toolbarId = getToolbarId(); final Toolbar toolbar = (Toolbar) findViewById(toolbarId); initToolbar(toolbar); } @Override public void setContentView(View view){ super.setContentView(view); final int toolbarId = getToolbarId(); final Toolbar toolbar = (Toolbar) findViewById(toolbarId); initToolbar(toolbar); } protected void initToolbar(Toolbar toolbar){ if(toolbar == null) return; setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); actionBar.setDisplayShowTitleEnabled(false); } addToolbarFragment(); } public void setToolbarTitle(CharSequence title){ if(statusFragment == null) return; statusFragment.setTitle(title); } public void setToolbarTitle(int titleResId){ if(statusFragment == null) return; statusFragment.setTitle(getString(titleResId)); } @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name){ } protected void addToolbarFragment(){ final int toolbarId = getToolbarId(); final FragmentManager fm = getSupportFragmentManager(); statusFragment = (VehicleStatusFragment) fm.findFragmentById(toolbarId); if(statusFragment == null){ statusFragment = new VehicleStatusFragment(); fm.beginTransaction().add(toolbarId, statusFragment).commit(); } } protected abstract int getToolbarId(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Context context = getApplicationContext(); dpApp = (DroidPlannerApp) getApplication(); lbm = LocalBroadcastManager.getInstance(context); mAppPrefs = DroidPlannerPrefs.getInstance(context); unitSystem = UnitManager.getUnitSystem(context); /* * Used to supplant wake lock acquisition (previously in * org.droidplanner.android.service .MAVLinkService) as suggested by the * android android.os.PowerManager#newWakeLock documentation. */ if (mAppPrefs.keepScreenOn()) { getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } setVolumeControlStream(AudioManager.STREAM_MUSIC); screenOrientation.unlock(); Utils.updateUILanguage(context); bindService(new Intent(context, AppService.class), this, Context.BIND_AUTO_CREATE); } @Override public void onDestroy() { super.onDestroy(); unbindService(this); lbm = null; } protected LocalBroadcastManager getBroadcastManager() { return lbm; } @Override public void onApiConnected() { invalidateOptionsMenu(); getBroadcastManager().registerReceiver(superReceiver, superIntentFilter); if (dpApp.getDrone().isConnected()) onDroneConnected(); else onDroneDisconnected(); lbm.sendBroadcast(new Intent(MissionProxy.ACTION_MISSION_PROXY_UPDATE)); } @Override public void onApiDisconnected() { getBroadcastManager().unregisterReceiver(superReceiver); onDroneDisconnected(); } protected void onDroneConnected() { invalidateOptionsMenu(); screenOrientation.requestLock(); } protected void onDroneDisconnected() { invalidateOptionsMenu(); screenOrientation.unlock(); } @Override protected void onStart() { super.onStart(); final Context context = getApplicationContext(); unitSystem = UnitManager.getUnitSystem(context); dpApp.addApiListener(this); } @Override public void onStop() { super.onStop(); dpApp.removeApiListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_super_activiy, menu); final MenuItem toggleConnectionItem = menu.findItem(R.id.menu_connect); Drone drone = dpApp.getDrone(); if (drone == null || !drone.isConnected()){ menu.setGroupEnabled(R.id.menu_group_connected, false); menu.setGroupVisible(R.id.menu_group_connected, false); toggleConnectionItem.setTitle(R.string.menu_connect); return super.onCreateOptionsMenu(menu); } menu.setGroupEnabled(R.id.menu_group_connected, true); menu.setGroupVisible(R.id.menu_group_connected, true); final MenuItem killSwitchItem = menu.findItem(R.id.menu_kill_switch); killSwitchItem.setEnabled(false); killSwitchItem.setVisible(false); final boolean isKillEnabled = mAppPrefs.isKillSwitchEnabled(); if(killSwitchItem != null && isKillEnabled) { CapabilityApi.getApi(drone).checkFeatureSupport(CapabilityApi.FeatureIds.KILL_SWITCH, new CapabilityApi.FeatureSupportListener() { @Override public void onFeatureSupportResult(String s, int i, Bundle bundle) { switch (i) { case CapabilityApi.FEATURE_SUPPORTED: killSwitchItem.setEnabled(true); killSwitchItem.setVisible(true); break; default: killSwitchItem.setEnabled(false); killSwitchItem.setVisible(false); break; } } }); } final boolean areMissionMenusEnabled = enableMissionMenus(); final MenuItem sendMission = menu.findItem(R.id.menu_upload_mission); sendMission.setEnabled(areMissionMenusEnabled); sendMission.setVisible(areMissionMenusEnabled); final MenuItem loadMission = menu.findItem(R.id.menu_download_mission); loadMission.setEnabled(areMissionMenusEnabled); loadMission.setVisible(areMissionMenusEnabled); toggleConnectionItem.setTitle(R.string.menu_disconnect); return super.onCreateOptionsMenu(menu); } protected boolean enableMissionMenus() { return false; } @Override public void onDialogYes(String dialogTag) { final Drone drone = dpApp.getDrone(); final MissionProxy missionProxy = dpApp.getMissionProxy(); switch(dialogTag){ case MISSION_UPLOAD_CHECK_DIALOG_TAG: missionProxy.addTakeOffAndRTL(); missionProxy.sendMissionToAPM(drone); break; } } @Override public void onDialogNo(String dialogTag) { final Drone drone = dpApp.getDrone(); final MissionProxy missionProxy = dpApp.getMissionProxy(); switch(dialogTag){ case MISSION_UPLOAD_CHECK_DIALOG_TAG: missionProxy.sendMissionToAPM(drone); break; } } @Override public boolean onOptionsItemSelected(MenuItem item) { final Drone dpApi = dpApp.getDrone(); switch (item.getItemId()) { case R.id.menu_connect: toggleDroneConnection(); return true; case R.id.menu_upload_mission: { final MissionProxy missionProxy = dpApp.getMissionProxy(); if (missionProxy.getItems().isEmpty() || missionProxy.hasTakeoffAndLandOrRTL()) { missionProxy.sendMissionToAPM(dpApi); } else { SupportYesNoWithPrefsDialog dialog = SupportYesNoWithPrefsDialog.newInstance( getApplicationContext(), MISSION_UPLOAD_CHECK_DIALOG_TAG, getString(R.string.mission_upload_title), getString(R.string.mission_upload_message), getString(android.R.string.ok), getString(R.string.label_skip), DroidPlannerPrefs.PREF_AUTO_INSERT_MISSION_TAKEOFF_RTL_LAND, this); if (dialog != null) { dialog.show(getSupportFragmentManager(), MISSION_UPLOAD_CHECK_DIALOG_TAG); } } return true; } case R.id.menu_download_mission: MissionApi.getApi(dpApi).loadWaypoints(); return true; case R.id.menu_kill_switch: SlideToUnlockDialog unlockDialog = SlideToUnlockDialog.newInstance("disable vehicle", new Runnable() { @Override public void run() { VehicleApi.getApi(dpApi).arm(false, true, new SimpleCommandListener() { @Override public void onError(int error) { final int errorMsgId; switch(error){ case CommandExecutionError.COMMAND_UNSUPPORTED: errorMsgId = R.string.error_kill_switch_unsupported; break; default: errorMsgId = R.string.error_kill_switch_failed; break; } Toast.makeText(getApplicationContext(), errorMsgId, Toast.LENGTH_LONG).show(); } @Override public void onTimeout() { Toast.makeText(getApplicationContext(), R.string.error_kill_switch_failed, Toast.LENGTH_LONG).show(); } }); } }); unlockDialog.show(getSupportFragmentManager(), "Slide to use the Kill Switch"); return true; case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; default: return super.onOptionsItemSelected(item); } } public void toggleDroneConnection() { final Drone drone = dpApp.getDrone(); if (drone != null && drone.isConnected()) dpApp.disconnectFromDrone(); else dpApp.connectToDrone(); } }