package com.openvehicles.OVMS.ui;
import java.util.UUID;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Window;
import com.luttu.AppPrefes;
import com.openvehicles.OVMS.R;
import com.openvehicles.OVMS.api.ApiObservable;
import com.openvehicles.OVMS.api.ApiObserver;
import com.openvehicles.OVMS.api.ApiService;
import com.openvehicles.OVMS.entities.CarData;
import com.openvehicles.OVMS.ui.FragMap.UpdateLocation;
import com.openvehicles.OVMS.ui.GetMapDetails.afterasytask;
import com.openvehicles.OVMS.ui.utils.Database;
import com.openvehicles.OVMS.utils.ConnectionList;
import com.openvehicles.OVMS.utils.ConnectionList.Con;
public class MainActivity extends ApiActivity implements
ActionBar.OnNavigationListener, afterasytask, Con, UpdateLocation {
private static final String TAG = "MainActivity";
private final Handler mC2dmHandler = new Handler();
private ViewPager mViewPager;
private MainPagerAdapter mPagerAdapter;
AppPrefes appPrefes;
Database database;
public static UpdateLocation updateLocation;
public GetMapDetails getMapDetails;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appPrefes = new AppPrefes(this, "ovms");
database = new Database(this);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setSupportProgressBarIndeterminateVisibility(false);
// OCM init:
updateLocation = this;
updatelocation();
String url = "http://api.openchargemap.io/v2/referencedata/";
ConnectionList connectionList = new ConnectionList(this, this, url,
true);
// set up receiver for server communication service:
registerReceiver(mApiEventReceiver, new IntentFilter(
getPackageName() + ".ApiEvent"));
// set up receiver for notifications:
Log.d(TAG, "Notifications registering receiver for Intent: " + getPackageName() + ".Notification");
registerReceiver(mNotificationReceiver, new IntentFilter(
getPackageName() + ".Notification"));
// init UI tabs:
mViewPager = new ViewPager(this);
mViewPager.setId(android.R.id.tabhost);
setContentView(mViewPager);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(false);
mPagerAdapter = new MainPagerAdapter(
new TabInfo(R.string.Battery, R.drawable.ic_action_battery, InfoFragment.class),
new TabInfo(R.string.Car, R.drawable.ic_action_car, CarFragment.class),
new TabInfo(R.string.Location, R.drawable.ic_action_location_map, FragMap.class),
new TabInfo(R.string.Messages, R.drawable.ic_action_email, NotificationsFragment.class),
new TabInfo(R.string.Settings, R.drawable.ic_action_settings, SettingsFragment.class)
);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
// cancel system notifications on page "Messages":
if (position == 3) {
NotificationManager mNotificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancelAll();
}
}
});
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setListNavigationCallbacks(
new NavAdapter(this, mPagerAdapter.getTabInfoItems()), this);
// check for C2DM registration
// Restore saved registration id
SharedPreferences settings = getSharedPreferences("C2DM", 0);
String registrationID = settings.getString("RegID", "");
if (registrationID.length() == 0) {
Log.d(TAG, "C2DM: Doing first time registration.");
// No C2DM ID available. Register now.
// ProgressDialog progress = ProgressDialog.show(this,
// "Push Notification Network", "Sending one-time registration...");
Intent intent = new Intent(
"com.google.android.c2dm.intent.REGISTER");
intent.putExtra("app",
PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // boilerplate
intent.putExtra("sender", "openvehicles@gmail.com");
startService(intent);
// progress.dismiss();
mC2dmHandler.postDelayed(mC2DMRegistrationID, 2000);
/*
* unregister Intent unregIntent = new
* Intent("com.google.android.c2dm.intent.UNREGISTER");
* unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0,
* new Intent(), 0)); startService(unregIntent);
*/
} else {
Log.d(TAG, "C2DM: Loaded Saved C2DM registration ID: "
+ registrationID);
mC2dmHandler.postDelayed(mC2DMRegistrationID, 2000);
}
onNewIntent(getIntent());
}
@Override
public void onNewIntent(Intent newIntent) {
super.onNewIntent(newIntent);
if (newIntent.getBooleanExtra("onNotification", false)) {
onNavigationItemSelected(3, 0);
}
}
@Override
protected void onDestroy() {
unregisterReceiver(mApiEventReceiver);
unregisterReceiver(mNotificationReceiver);
database.close();
super.onDestroy();
}
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
TabInfo ti = mPagerAdapter.getTabInfoItems()[itemPosition];
getSupportActionBar().setIcon(ti.icon_res_id);
mViewPager.setCurrentItem(itemPosition, false);
return true;
}
private final Runnable mC2DMRegistrationID = new Runnable() {
public void run() {
// check if tcp connection is still active (it may be closed as the
// user leaves the program)
ApiService service = getService();
if (service == null || !service.isLoggedIn())
return;
SharedPreferences settings = getSharedPreferences("C2DM", 0);
String registrationID = settings.getString("RegID", "");
String uuid;
if (!settings.contains("UUID")) {
// generate a new UUID
uuid = UUID.randomUUID().toString();
Editor editor = getSharedPreferences("C2DM",
Context.MODE_PRIVATE).edit();
editor.putString("UUID", uuid);
editor.commit();
Log.d(TAG, "C2DM: Generated New C2DM UUID: " + uuid);
} else {
uuid = settings.getString("UUID", "");
Log.d(TAG, "C2DM: Loaded Saved C2DM UUID: " + uuid);
}
// MP-0
// p<appid>,<pushtype>,<pushkeytype>{,<vehicleid>,<netpass>,<pushkeyvalue>}
CarData carData = service.getCarData();
String cmd = String.format("MP-0 p%s,c2dm,production,%s,%s,%s",
uuid, carData.sel_vehicleid, carData.sel_server_password,
registrationID);
service.sendCommand(cmd, null);
if ((registrationID.length() == 0)
|| !service.sendCommand(cmd, null)) {
// command not successful, reschedule reporting after 5 seconds
Log.d(TAG, "C2DM: Reporting C2DM ID failed. Rescheduling.");
mC2dmHandler.postDelayed(mC2DMRegistrationID, 5000);
}
}
};
private AlertDialog mApiErrorDialog;
private String mApiErrorMessage;
private final BroadcastReceiver mApiEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getSerializableExtra("onServerSocketError") != null) {
setSupportProgressBarIndeterminateVisibility(false);
// check if this message needs to be displayed:
String message = intent.getStringExtra("message");
if (mApiErrorDialog == null || (mApiErrorDialog != null &&
(!mApiErrorDialog.isShowing()
|| !message.equals(mApiErrorMessage)))) {
mApiErrorDialog = new AlertDialog.Builder(MainActivity.this)
.setTitle(R.string.Error)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show();
mApiErrorMessage = message;
}
}
if (intent.getBooleanExtra("onLoginBegin", false)) {
setSupportProgressBarIndeterminateVisibility(true);
// observe for login success:
ApiObservable.get().addObserver(mApiObserver);
}
}
};
private ApiObserver mApiObserver = new ApiObserver() {
@Override
public void update(CarData pCarData) {
// data update implies login successful => hide progress:
setSupportProgressBarIndeterminateVisibility(false);
// ...and hide error dialog:
if (mApiErrorDialog != null && mApiErrorDialog.isShowing()) {
mApiErrorDialog.hide();
}
// done waiting for login:
ApiObservable.get().deleteObserver(this);
}
@Override
public void onServiceAvailable(ApiService pService) {
}
};
private class TabInfo {
public final int title_res_id, icon_res_id;
public final Class<? extends BaseFragment> fragment_class;
public Fragment fragment;
public String getFragmentName() {
return fragment_class.getName();
}
public TabInfo(int pTitleResId, int pIconResId,
Class<? extends BaseFragment> pFragmentClass) {
title_res_id = pTitleResId;
icon_res_id = pIconResId;
fragment_class = pFragmentClass;
fragment = null;
}
@Override
public String toString() {
return getString(title_res_id);
}
}
private static class NavAdapter extends ArrayAdapter<TabInfo> {
public NavAdapter(Context context, TabInfo[] objects) {
super(context, R.layout.sherlock_spinner_item, objects);
setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);
}
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
TextView tv = (TextView) super.getDropDownView(position,
convertView, parent);
tv.setCompoundDrawablesWithIntrinsicBounds(
getItem(position).icon_res_id, 0, 0, 0);
return tv;
}
}
private class MainPagerAdapter extends FragmentPagerAdapter {
private final TabInfo[] mTabInfoItems;
public MainPagerAdapter(TabInfo... pTabInfoItems) {
super(getSupportFragmentManager());
mTabInfoItems = pTabInfoItems;
}
public TabInfo[] getTabInfoItems() {
return mTabInfoItems;
}
@Override
public Fragment getItem(int pPosition) {
if (mTabInfoItems[pPosition].fragment == null) {
// instantiate fragment:
mTabInfoItems[pPosition].fragment = Fragment.instantiate(
MainActivity.this,
mTabInfoItems[pPosition].getFragmentName());
}
//Log.d(TAG, "MainPagerAdapter: pos=" + pPosition + " => frg=" + mTabInfoItems[pPosition].fragment);
return mTabInfoItems[pPosition].fragment;
}
@Override
public int getCount() {
return mTabInfoItems.length;
}
@Override
public CharSequence getPageTitle(int pPosition) {
return getString(mTabInfoItems[pPosition].title_res_id);
}
}
@Override
public void connections(String al, String name) {
// TODO Auto-generated method stub
}
@Override
public void after(boolean flBoolean) {
// TODO Auto-generated method stub
}
// Make notification updates visible immediately:
private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//Log.d(TAG, "Notifications: received " + intent.toString());
// show messages list:
// ...or better not, as it can interrupt user activity...
//onNavigationItemSelected(3, 0);
// update messages list:
NotificationsFragment frg = (NotificationsFragment) mPagerAdapter.getItem(3);
if (frg != null) {
//Log.d(TAG, "Notifications: calling frg.update()");
frg.update();
}
}
};
@Override
public void updatelocation() {
// get car location:
String lat = "37.410866";
String lng = "-122.001946";
if (appPrefes.getData("lat_main").equals("")) {
// init car position:
appPrefes.SaveData("lat_main", lat);
appPrefes.SaveData("lng_main", lng);
Log.i(TAG, "updatelocation: init car position");
} else {
// get current car position:
lat = appPrefes.getData("lat_main");
lng = appPrefes.getData("lng_main");
}
// As OCM does not yet support incremental queries,
// we're using a cache with key = int(lat/lng)
// resulting in a tile size of max. 112 x 112 km
// = diagonal max 159 km
// The API call will fetch a fixed radius of 160 km
// covering all adjacent tiles.
// check OCM cache for key int(lat/lng):
boolean cache_valid = true;
String[] strings = lat.split("\\.");
String[] strings2 = lng.split("\\.");
int latitude = Integer.parseInt(strings[0]);
int longitude = Integer.parseInt(strings2[0]);
Cursor cursor = database.getlatlngdetail(latitude, longitude);
if (cursor.getCount() == 0) {
cache_valid = false;
}
else if (cursor.moveToFirst()) {
// check if last tile update was more than 4 weeks (28 days) ago:
long last_update = cursor.getLong(cursor.getColumnIndex("last_update"));
long now = System.currentTimeMillis() / 1000;
if (now > last_update + (3600 * 24 * 28))
cache_valid = false;
}
if (cache_valid) {
Log.d(TAG, "getdata: cache valid for lat/lng=" + latitude + "/" + longitude
+ ", lat_main=" + appPrefes.getData("lat_main"));
} else {
database.addlatlngdetail(latitude, longitude);
// make OCM API URL:
String maxresults = appPrefes.getData("maxresults");
String url = "http://api.openchargemap.io/v2/poi/?output=json&verbose=false"
+ "&latitude=" + lat
+ "&longitude=" + lng
+ "&distance=160" // see above
+ "&distanceunit=KM"
+ "&maxresults=" + (maxresults.equals("") ? "500" : maxresults);
Log.d(TAG, "getdata: new fetch for lat/lng=" + latitude + "/" + longitude
+ ", lat_main=" + appPrefes.getData("lat_main")
+ " => url=" + url);
// cancel old fetcher task:
if (getMapDetails != null) {
getMapDetails.cancel(true);
}
// create new fetcher task:
getMapDetails = new GetMapDetails(MainActivity.this, url, this);
// After Android 3.0, the default behavior of AsyncTask is execute in a single
// thread using SERIAL_EXECUTOR. This means the thread will no longer run while
// the App is in the foreground...
// Solution source:
// http://stackoverflow.com/questions/13080367/android-async-task-behavior-in-2-3-3-and-4-0-os
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
getMapDetails.execute();
} else {
getMapDetails.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}
}