/** AirCasting - Share your Air! Copyright (C) 2011-2012 HabitatMap, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. You can contact the authors by email at <info@habitatmap.org> */ package pl.llp.aircasting.activity; import pl.llp.aircasting.Intents; import pl.llp.aircasting.R; import pl.llp.aircasting.activity.adapter.SessionAdapter; import pl.llp.aircasting.activity.adapter.SessionAdapterFactory; import pl.llp.aircasting.activity.events.SessionLoadedEvent; import pl.llp.aircasting.activity.menu.MainMenu; import pl.llp.aircasting.activity.task.CalibrateSessionsTask; import pl.llp.aircasting.activity.task.OpenSessionTask; import pl.llp.aircasting.event.SyncStateChangedEvent; import pl.llp.aircasting.helper.SelectSensorHelper; import pl.llp.aircasting.helper.SettingsHelper; import pl.llp.aircasting.helper.TopBarHelper; import pl.llp.aircasting.model.Sensor; import pl.llp.aircasting.model.SensorManager; import pl.llp.aircasting.model.Session; import pl.llp.aircasting.model.SessionManager; import pl.llp.aircasting.receiver.SyncBroadcastReceiver; import pl.llp.aircasting.storage.db.UncalibratedMeasurementCalibrator; import pl.llp.aircasting.storage.repository.SensorRepository; import pl.llp.aircasting.storage.repository.SessionRepository; import pl.llp.aircasting.util.SyncState; import android.app.Application; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.Spinner; import android.widget.Toast; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; import org.jetbrains.annotations.Nullable; import roboguice.inject.InjectResource; import roboguice.inject.InjectView; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newLinkedList; public class SessionsActivity extends RoboListActivityWithProgress implements AdapterView.OnItemLongClickListener, AdapterView.OnItemSelectedListener { private static final int ALL_ID = 0; @Inject SessionAdapterFactory sessionAdapterFactory; @Inject SelectSensorHelper selectSensorHelper; @Inject SessionRepository sessionRepository; @Inject SensorRepository sensorRepository; @Inject SessionManager sessionManager; @Inject SettingsHelper settingsHelper; @Inject SensorManager sensorManager; @Inject TopBarHelper topBarHelper; @Inject Application context; @Inject SyncState syncState; @Inject EventBus eventBus; @Inject MainMenu mainMenu; @Inject UncalibratedMeasurementCalibrator calibrator; @InjectResource(R.string.all) String all; @InjectView(R.id.sensor_spinner) Spinner sensorSpinner; @InjectView(R.id.sync_summary) Button syncSummary; @InjectView(R.id.top_bar) View topBar; @Inject SyncBroadcastReceiver syncBroadcastReceiver; BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { refreshList(); } }; private SensorsAdapter sensorAdapter; private SessionAdapter sessionAdapter; private Sensor selectedSensor; private long sessionId; private boolean calibrationAttempted; private DummySensor dummySensor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); calibrateOldRecords(); setContentView(R.layout.sessions); getListView().setOnItemLongClickListener(this); dummySensor = new DummySensor(all); sensorAdapter = new SensorsAdapter(this, Lists.<SensorWrapper>newArrayList(), dummySensor); sensorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); sensorAdapter.insert(dummySensor, ALL_ID); sensorSpinner.setAdapter(sensorAdapter); } @Override protected void onResume() { super.onResume(); refreshList(); refreshTopBar(); IntentFilter filter = new IntentFilter(); filter.addAction(Intents.ACTION_SYNC_UPDATE); registerReceiver(broadcastReceiver, filter); registerReceiver(syncBroadcastReceiver, SyncBroadcastReceiver.INTENT_FILTER); eventBus.register(this); topBar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(SessionsActivity.this, ThresholdsActivity.class); intent.putExtra(Intents.EXTRA_SENSOR, selectedSensor); startActivity(intent); } }); syncSummary.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intents.triggerSync(SessionsActivity.this); } }); } private void refreshTopBar() { if (selectedSensor == null) { topBar.setVisibility(View.GONE); } else { topBar.setVisibility(View.VISIBLE); topBarHelper.updateTopBar(selectedSensor, topBar); } } private void refreshItems() { List<Session> sessions = sessionRepository.notDeletedSessions(selectedSensor); if (sessionAdapter == null) { sessionAdapter = sessionAdapterFactory.getSessionAdapter(this); setListAdapter(sessionAdapter); } sessionAdapter.setSessions(sessions); sessionAdapter.setSelectedSensor(selectedSensor); } @Override protected void onPause() { super.onPause(); unregisterReceiver(broadcastReceiver); unregisterReceiver(syncBroadcastReceiver); eventBus.unregister(this); } private void refreshList() { runOnUiThread(new Runnable() { @Override public void run() { refreshBottomBar(); refreshSensors(); refreshItems(); } }); } private void refreshSensors() { List<Sensor> sensors = sensorRepository.getAll(); Iterable<SensorWrapper> wrappers = Iterables.transform(sensors, new Function<Sensor, SensorWrapper>() { @Override public SensorWrapper apply(@Nullable Sensor input) { return new SensorWrapper(input); } }); List<SensorWrapper> wrapperList = newArrayList(wrappers); sensorAdapter.update(wrapperList); sensorSpinner.setPromptId(R.string.select_sensor); // sensorSpinner.setAdapter(sensorAdapter); sensorSpinner.setOnItemSelectedListener(this); } @Subscribe public void onEvent(SyncStateChangedEvent event) { runOnUiThread(new Runnable() { @Override public void run() { refreshBottomBar(); } }); } private void refreshBottomBar() { if (syncState.isInProgress()) { syncSummary.setText(R.string.sync_in_progress); } else { syncSummary.setText(R.string.sync_possible); } } @Override protected void onListItemClick(ListView listView, View view, int position, long id) { Session s = sessionAdapter.getSession(position); if (s.isFixed()) Toast.makeText(context, "You can view fixed-location sessions only on the web app.", Toast.LENGTH_LONG).show(); else viewSession(s.getId()); } @Override public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) { Session s = sessionAdapter.getSession(position); sessionId = s.getId(); Intent intent; if (s.isFixed()) intent = new Intent(this, OpenFixedSessionActivity.class); else intent = new Intent(this, OpenMobileSessionActivity.class); startActivityForResult(intent, 0); return true; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (resultCode) { case R.id.view: viewSession(sessionId); break; case R.id.delete_session: deleteSession(sessionId); break; case R.id.edit: editSession(sessionId); break; case R.id.save_button: updateSession(data); break; case R.id.share: Intents.shareSession(this, sessionId); break; case R.id.continue_streaming: continueAircastingSession(sessionId); finish(); break; } } private void updateSession(Intent data) { Session session = Intents.editSessionResult(data); sessionRepository.update(session); Intents.triggerSync(context); refreshList(); } private void continueAircastingSession(long id) { Session session = sessionRepository.loadShallow(id); sessionManager.continueStreamingSession(session, true); } private void editSession(long id) { Session session = sessionRepository.loadShallow(id); Intents.editSession(this, session); } private void deleteSession(long id) { sessionRepository.markSessionForRemoval(id); Intents.triggerSync(context); refreshList(); } @Override public boolean onCreateOptionsMenu(Menu menu) { return mainMenu.create(this, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { return mainMenu.handleClick(this, item); } private void viewSession(long id) { if (sessionManager.isSessionStarted()) { Toast.makeText(context, R.string.stop_aircasting, Toast.LENGTH_LONG).show(); return; } new OpenSessionTask(this) { @Override protected Session doInBackground(Long... longs) { sessionManager.loadSession(longs[0], this); return null; } @Override protected void onPostExecute(Session session) { super.onPostExecute(session); startSessionView(); } }.execute(id); } private void startSessionView() { eventBus.post(new SessionLoadedEvent(sessionManager.getSession())); startActivity(new Intent(context, StreamsActivity.class)); } @Override protected Dialog onCreateDialog(int id) { switch (id) { case SelectSensorHelper.DIALOG_ID: return selectSensorHelper.chooseSensor(this); default: return super.onCreateDialog(id); } } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedSensor = sensorAdapter.getItem(position).getSensor(); refreshItems(); refreshTopBar(); } @Override public void onNothingSelected(AdapterView<?> parent) { } private static class SensorWrapper { private Sensor sensor; public SensorWrapper(Sensor sensor) { this.sensor = sensor; } public Sensor getSensor() { return sensor; } @Override public String toString() { return sensor.getShortType() + " - " + sensor.getSensorName(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SensorWrapper that = (SensorWrapper) o; if (!sensor.equals(that.sensor)) return false; return true; } @Override public int hashCode() { return sensor.hashCode(); } } private static class DummySensor extends SensorWrapper { private String name; public DummySensor(String name) { super(null); this.name = name; } @Override public String toString() { return name; } } private void calibrateOldRecords() { if(calibrationAttempted) return; calibrationAttempted = true; if(calibrator.sessionsToCalibrate() > 0) { CalibrateSessionsTask task = new CalibrateSessionsTask(this, calibrator); task.execute(); } } static class SensorsAdapter extends ArrayAdapter<SensorWrapper> { private final ArrayList<SensorWrapper> data; private final SensorWrapper dummy; public SensorsAdapter(Context context, ArrayList<SensorWrapper> data, SensorWrapper dummy) { super(context, android.R.layout.simple_spinner_item, data); this.data = data; this.dummy = dummy; } public void update(List<SensorWrapper> newWrappers) { LinkedList<SensorWrapper> toRemove = newLinkedList(); for (SensorWrapper wrapper : data) { if(dummy != wrapper && !newWrappers.contains(wrapper)) { toRemove.add(wrapper); } } for (SensorWrapper wrapper : toRemove) { remove(wrapper); } for (SensorWrapper potentialWrapper : newWrappers) { if(getPosition(potentialWrapper) < 0) { add(potentialWrapper); } } } } }