package org.ovirt.mobile.movirt.ui;
import android.app.DialogFragment;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import com.google.zxing.Result;
import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;
import org.androidannotations.annotations.OptionsItem;
import org.androidannotations.annotations.OptionsMenu;
import org.androidannotations.annotations.ViewById;
import org.ovirt.mobile.movirt.R;
import org.ovirt.mobile.movirt.camera.AmbientLightManager;
import org.ovirt.mobile.movirt.camera.BeepManager;
import org.ovirt.mobile.movirt.camera.CameraManager;
import org.ovirt.mobile.movirt.camera.CaptureActivityHandler;
import org.ovirt.mobile.movirt.camera.InactivityTimer;
import org.ovirt.mobile.movirt.camera.PreferencesActivity_;
import org.ovirt.mobile.movirt.camera.ViewfinderView;
import org.ovirt.mobile.movirt.facade.HostFacade;
import org.ovirt.mobile.movirt.model.Event;
import org.ovirt.mobile.movirt.model.Host;
import org.ovirt.mobile.movirt.model.Vm;
import org.ovirt.mobile.movirt.provider.ProviderFacade;
import org.ovirt.mobile.movirt.ui.dialogs.ErrorDialogFragment;
import org.ovirt.mobile.movirt.ui.events.EventsCursorAdapter;
import org.ovirt.mobile.movirt.util.CursorAdapterLoader;
import java.io.IOException;
import static org.ovirt.mobile.movirt.provider.OVirtContract.BaseEntity.ID;
import static org.ovirt.mobile.movirt.provider.OVirtContract.SnapshotEmbeddableEntity.SNAPSHOT_ID;
import static org.ovirt.mobile.movirt.provider.OVirtContract.Vm.HOST_ID;
import static org.ovirt.mobile.movirt.provider.OVirtContract.Vm.NAME;
import static org.ovirt.mobile.movirt.provider.OVirtContract.Vm.STATUS;
@EActivity(R.layout.activity_camera)
@OptionsMenu(R.menu.camera)
public class CameraActivity extends MovirtActivity implements SurfaceHolder.Callback {
private static final String TAG = CameraActivity.class.getSimpleName();
private static final long SCAN_DELAY_MS = 100L;
private static final int EVENTS_LOADER = FIRST_CHILD_LOADER;
private static final int VMS_LOADER = FIRST_CHILD_LOADER + 1;
private static final int HOSTS_LOADER = FIRST_CHILD_LOADER + 2;
@Bean
HostFacade hostFacade;
@ViewById
TextView textHostName;
@ViewById
TextView textStatus;
@ViewById
TextView textCpuUsage;
@ViewById
TextView textMemoryUsage;
@ViewById
LinearLayout panelDetails;
@ViewById(R.id.viewfinder_view)
ViewfinderView viewfinderView;
@ViewById
ListView panelEvents;
@ViewById
LinearLayout panelParent;
@ViewById
ImageView imageStatus;
@ViewById
LinearLayout panelVms;
@ViewById
ListView listVms;
@ViewById
ProgressBar progress;
@ViewById(R.id.result_text)
TextView resultTextView;
private CameraManager cameraManager;
private CaptureActivityHandler handler;
private BeepManager beepManager;
private InactivityTimer inactivityTimer;
private AmbientLightManager ambientLightManager;
private String lastResult;
private boolean hasSurface;
private Host lastHost;
private CursorAdapterLoader cursorEventsAdapterLoader;
private CursorAdapterLoader cursorVmsAdapterLoader;
private LoaderManager loaderManager;
private View currentView;
private int eventsPage = 1;
private int vmsPage = 1;
private HostsLoader hostsLoader;
@AfterViews
void init() {
//set visibility
panelEvents.setVisibility(View.GONE);
panelParent.setVisibility(View.GONE);
panelVms.setVisibility(View.GONE);
currentView = panelDetails;
setProgressBar(progress);
loaderManager = getSupportLoaderManager();
hostsLoader = new HostsLoader();
loaderManager.initLoader(HOSTS_LOADER, null, hostsLoader);
//init events
SimpleCursorAdapter eventListAdapter = new EventsCursorAdapter(this);
panelEvents.setAdapter(eventListAdapter);
panelEvents.setOnScrollListener(new EndlessScrollListener() {
@Override
public void onLoadMore(int page, int totalItemsCount) {
eventsPage = page;
loaderManager.restartLoader(EVENTS_LOADER, null, cursorEventsAdapterLoader);
}
});
cursorEventsAdapterLoader = new CursorAdapterLoader(eventListAdapter) {
@Override
public synchronized Loader<Cursor> onCreateLoader(int id, Bundle args) {
final ProviderFacade.QueryBuilder<Event> query = providerFacade.query(Event.class);
if (lastHost != null) {
query.where(HOST_ID, lastHost.getId());
}
return query.orderByDescending(ID).limit(eventsPage * 20).asLoader();
}
};
loaderManager.initLoader(EVENTS_LOADER, null, cursorEventsAdapterLoader);
//init vms
SimpleCursorAdapter vmListAdapter = new SimpleCursorAdapter(this,
R.layout.vm_list_item_small,
null,
new String[]{NAME, STATUS},
new int[]{R.id.vm_name, R.id.vm_status}, 0);
vmListAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (columnIndex == cursor.getColumnIndex(NAME)) {
TextView textView = (TextView) view;
String vmName = cursor.getString(cursor.getColumnIndex(NAME));
textView.setText(vmName);
} else if (columnIndex == cursor.getColumnIndex(STATUS)) {
ImageView imageView = (ImageView) view;
Vm.Status status = Vm.Status.valueOf(cursor.getString(cursor.getColumnIndex(STATUS)));
imageView.setImageResource(status.getResource());
}
return true;
}
});
listVms.setAdapter(vmListAdapter);
listVms.setOnScrollListener(new EndlessScrollListener() {
@Override
public void onLoadMore(int page, int totalItemsCount) {
vmsPage = page;
loaderManager.restartLoader(VMS_LOADER, null, cursorVmsAdapterLoader);
}
});
cursorVmsAdapterLoader = new CursorAdapterLoader(vmListAdapter) {
@Override
public synchronized Loader<Cursor> onCreateLoader(int id, Bundle args) {
final ProviderFacade.QueryBuilder<Vm> query = providerFacade.query(Vm.class).empty(SNAPSHOT_ID);
if (lastHost == null) {
return query.where(HOST_ID, "0").asLoader();
} else {
query.where(HOST_ID, lastHost.getId());
}
return query.orderByAscending(NAME).limit(vmsPage * 20).asLoader();
}
};
loaderManager.initLoader(VMS_LOADER, null, cursorVmsAdapterLoader);
}
@Override
public void restartLoader() {
super.restartLoader();
loaderManager.restartLoader(HOSTS_LOADER, null, hostsLoader);
}
@Override
public void destroyLoader() {
super.destroyLoader();
loaderManager.destroyLoader(HOSTS_LOADER);
loaderManager.destroyLoader(EVENTS_LOADER);
loaderManager.destroyLoader(VMS_LOADER);
}
public CameraManager getCameraManager() {
return cameraManager;
}
ViewfinderView getViewfinderView() {
return viewfinderView;
}
public Handler getHandler() {
return handler;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.activity_camera);
hasSurface = false;
beepManager = new BeepManager(this);
inactivityTimer = new InactivityTimer(this);
ambientLightManager = new AmbientLightManager(this);
PreferenceManager.setDefaultValues(this, R.xml.preferences_zxing, false);
}
@Override
protected void onResume() {
super.onResume();
// CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
// want to open the camera driver and measure the screen size if we're going to show the help on
// first launch. That led to bugs where the scanning rectangle was the wrong size and partially
// off screen.
cameraManager = new CameraManager(getApplication());
viewfinderView.setCameraManager(cameraManager);
lastResult = null;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
beepManager.updatePrefs();
inactivityTimer.onResume();
ambientLightManager.start(cameraManager);
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.camera_preview);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
// The activity was paused but not stopped, so the surface still exists. Therefore
// surfaceCreated() won't be called, so init the camera here.
initCamera(surfaceHolder);
} else {
// Install the callback and wait for surfaceCreated() to init the camera.
surfaceHolder.addCallback(this);
}
}
@Override
protected void onPause() {
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
cameraManager.closeDriver();
beepManager.close();
inactivityTimer.onPause();
ambientLightManager.stop();
if (!hasSurface) {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.camera_preview);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
surfaceHolder.removeCallback(this);
}
super.onPause();
}
@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_FOCUS:
case KeyEvent.KEYCODE_CAMERA:
// Handle these events so they don't launch the Camera app
return true;
// Use volume up/down to turn on light
case KeyEvent.KEYCODE_VOLUME_DOWN:
cameraManager.setTorch(false);
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
cameraManager.setTorch(true);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (holder == null) {
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
}
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
private void initCamera(SurfaceHolder surfaceHolder) {
if (surfaceHolder == null) {
throw new IllegalStateException("No SurfaceHolder provided");
}
if (cameraManager.isOpen()) {
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
return;
}
try {
cameraManager.openDriver(surfaceHolder);
// Creating the handler starts the preview, which can also throw a RuntimeException.
if (handler == null) {
handler = new CaptureActivityHandler(this, cameraManager);
}
} catch (IOException ioe) {
Log.w(TAG, ioe);
displayFrameworkBugMessage(ioe.getMessage());
} catch (RuntimeException e) {
// Barcode Scanner has seen crashes in the wild of this variety:
// java.?lang.?RuntimeException: Fail to connect to camera service
Log.w(TAG, "Unexpected error initializing camera", e);
displayFrameworkBugMessage(e.getMessage());
}
}
private void displayFrameworkBugMessage(String errorDetails) {
DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
getString(com.google.zxing.client.android.R.string.zxing_msg_camera_framework_bug) +
"\n\n" + errorDetails
);
dialogFragment.show(getFragmentManager(), "camera_error");
}
/**
* A valid barcode has been found, so give an indication of success and show the results.
*
* @param rawResult The contents of the barcode.
* @param barcode A greyscale bitmap of the camera data which was decoded.
*/
public void handleDecode(Result rawResult, Bitmap barcode) {
inactivityTimer.onActivity();
String result = rawResult.getText();
if (result != null) {
result = result.trim().toLowerCase();
viewfinderView.drawResultBitmap(barcode, rawResult.getResultPoints());
if (!result.equals(lastResult)) {
lastResult = result;
beepManager.playBeepSoundAndVibrate();
resultTextView.setText(lastResult);
loaderManager.restartLoader(HOSTS_LOADER, null, hostsLoader);
}
}
restartPreviewAfterDelay(SCAN_DELAY_MS);
}
public void restartPreviewAfterDelay(long delayMS) {
if (handler != null) {
handler.sendEmptyMessageDelayed(
CaptureActivityHandler.ZXING_RESTART, delayMS);
}
}
public void drawViewfinder() {
viewfinderView.drawViewfinder();
}
@OptionsItem(R.id.menu_settings)
public void openSettings() {
Intent intent = new Intent(this, PreferencesActivity_.class);
startActivity(intent);
}
@Click
void buttonOpenHostDetails() {
if (lastHost != null) {
startActivity(hostFacade.getDetailIntent(lastHost, this));
}
}
@Click
void buttonSwitch(View view) {
currentView.setVisibility(View.GONE);
if (currentView == panelDetails) {
((Button) view).setText(R.string.buttonSwitchDetails);
currentView = panelEvents;
} else {
((Button) view).setText(R.string.buttonSwitchEvents);
currentView = panelDetails;
}
currentView.setVisibility(View.VISIBLE);
}
private void renderDetails(Host host) {
textHostName.setText(host.getName());
textStatus.setText(host.getStatus().toString());
textCpuUsage.setText(String.format("%.2f%%", host.getCpuUsage()));
textMemoryUsage.setText(String.format("%.2f%%", host.getMemoryUsage()));
//show status icon
Host.Status status = host.getStatus();
imageStatus.setImageResource(status.getResource());
panelParent.setVisibility(View.VISIBLE);
currentView.setVisibility(View.VISIBLE);
panelVms.setVisibility(View.VISIBLE);
}
private class HostsLoader implements LoaderManager.LoaderCallbacks<Cursor> {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (lastResult == null) {
return providerFacade.query(Host.class).id("0").asLoader();
} else {
return providerFacade.query(Host.class).id(lastResult).asLoader();
}
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data != null && data.getCount() > 0) {
data.moveToFirst();
lastHost = new Host();
lastHost.initFromCursor(data);
loaderManager.restartLoader(EVENTS_LOADER, null, cursorEventsAdapterLoader);
loaderManager.restartLoader(VMS_LOADER, null, cursorVmsAdapterLoader);
renderDetails(lastHost);
} else if (lastResult != null) {
resultTextView.setText(lastResult + '\n' + getString(R.string.no_such_host));
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
}