You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
11 KiB

package haus.nightmare.GlassTesla;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.google.android.glass.media.Sounds;
import com.google.android.glass.widget.CardBuilder;
import com.google.android.glass.widget.CardScrollAdapter;
import com.google.android.glass.widget.CardScrollView;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends Activity {
private static final String TAG = "GT-Restart";
private CardScrollView cardScroller;
private TeslaResponse vehicleData;
private SharedPreferences prefs;
private ArrayList<View> scrollerViews;
private SyncTeslaService mService;
private boolean mBound = false;
@SuppressLint("SimpleDateFormat")
private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
/** Defines callbacks for service binding, passed to bindService() */
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
SyncTeslaService.SyncTeslaBinder binder = (SyncTeslaService.SyncTeslaBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
@Override
protected void onCreate(final Bundle bundle) {
super.onCreate(bundle);
prefs = getPreferences(MODE_PRIVATE);
Intent intent = new Intent(this, SyncTeslaService.class);
startService(intent);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
scrollerViews = new ArrayList<View>();
cardScroller = new CardScrollView(getApplicationContext());
scrollerViews.add(buildLoadingView());
scrollerViews.add(buildLocksView());
scrollerViews.add(buildControlView());
cardScroller.setAdapter(new CardScrollAdapter() {
@Override
public int getCount() {
return scrollerViews.size();
}
@Override
public Object getItem(int position) {
return scrollerViews.get(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return scrollerViews.get(position);
}
@Override
public int getPosition(Object item) {
return scrollerViews.indexOf(item);
}
});
cardScroller.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
switch(position) {
case 2:
postCommand("flash_lights");
break;
case 1:
if (vehicleData.vehicle_state.locked) {
postCommand("door_unlock");
} else {
postCommand("door_lock");
}
break;
default:
updateIfExpired();
}
}
});
cardScroller.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
switch (position) {
case 2:
postCommand("honk_horn");
break;
case 1:
postCommand("set_sentry_mode?on=" +
!vehicleData.vehicle_state.sentry_mode);
break;
default:
postCommand("remote_start_drive?password=" + prefs.getString("auth_password", ""));
}
return true;
}
});
setContentView(cardScroller);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, SyncTeslaService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(connection);
mBound = false;
}
@Override
protected void onResume() {
super.onResume();
if (this.cardScroller != null) {
cardScroller.activate();
} else {
updateIfExpired();
}
}
private void updateIfExpired() {
if (mBound) {
Calendar expire = Calendar.getInstance();
expire.add(Calendar.MINUTE, -5);
vehicleData = mService.getVehicleData();
if (vehicleData == null || new Date(vehicleData.vehicle_state.timestamp).after(expire.getTime())) {
refreshCards();
} else {
updateCards();
}
} else {
refreshCards();
}
}
@Override
protected void onPause() {
if (cardScroller != null) {
cardScroller.deactivate();
}
super.onPause();
}
private void playAudio(int sound) {
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
if (am != null) {
am.playSoundEffect(sound);
}
}
private void refreshCards() {
Thread t = new Thread(new Runnable(){
public void run() {
mService.updateSharedPrefs(prefs);
mService.loadVehicleData(prefs.getString("vehicle_id", ""));
vehicleData = mService.getVehicleData();
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
updateCards();
}
private void updateCards() {
scrollerViews = new ArrayList<View>();
scrollerViews.add(buildStatusView());
scrollerViews.add(buildLocksView());
scrollerViews.add(buildControlView());
cardScroller.animate(0, CardScrollView.Animation.INSERTION);
}
private View buildControlView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
card.setText("Tap to flash lights, long press to honk.");
return card.getView();
}
private View buildLoadingView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
card.setText("Tap to load data.");
return card.getView();
}
private View buildLocksView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
String body;
if (vehicleData == null) {
body = "Tap to lock";
} else {
body = "Tap to " + (vehicleData.vehicle_state.locked ? "unlock":"lock");
}
body += "\nLong press to ";
if (vehicleData == null) {
body += "enable";
} else {
body += vehicleData.vehicle_state.sentry_mode ? "disable" : "enable";
}
body += " Sentry Mode";
card.setText(body);
return card.getView();
}
private View buildStatusView() {
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT);
String chargeDuration = "";
int totalSecs = (int) (vehicleData.charge_state.time_to_full_charge * 3600);
int hours = totalSecs / 3600;
int minutes = (totalSecs % 3600) / 60;
if (hours > 0) {
chargeDuration += hours + "h";
}
if (minutes > 0) {
chargeDuration += minutes + "m";
}
if (vehicleData.charge_state.charging_state.equalsIgnoreCase("Disconnected")) {
chargeDuration = "N/A";
}
Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, hours);
cal.add(Calendar.MINUTE, minutes);
card.setText(
String.format(getString(R.string.StatusFormat),
vehicleData.charge_state.battery_level,
(int) vehicleData.charge_state.battery_range,
vehicleData.vehicle_state.sentry_mode ? "Enabled" : "Disabled",
vehicleData.charge_state.charging_state,
vehicleData.charge_state.charger_power,
chargeDuration,
timeFormatter.format(cal.getTime())
)
);
card.setFootnote(String.format("%s last updated: %s",
vehicleData.display_name,
timeFormatter.format(vehicleData.vehicle_state.timestamp)
));
View v = card.getView();
v.setKeepScreenOn(true);
return v;
}
public void postCommand(String command) {
AsyncTask<String, ArrayList<View>, Boolean> task = new AsyncTask<String, ArrayList<View>, Boolean>() {
@Override
protected Boolean doInBackground(String... commands) {
String command = commands[0];
try {
String token = prefs.getString("bearer_token", "");
String vehicleId = prefs.getString("vehicle_id", "");
URL object = new URL("https://owner-api.teslamotors.com/api/1/vehicles/"
+ vehicleId +"/command/" + command);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(object)
.method("POST", RequestBody.create(null, new byte[]{}))
.addHeader("Authorization", "Bearer " + token)
.addHeader("User-Agent", "Tesla-GLASS")
.addHeader("Content-Type", "application/json")
.build();
Response response = client.newCall(request).execute();
if (response.code() == 408) {
postCommand("wake_up");
}
return response.code() == 200;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPreExecute() {
cardScroller.animate(0, CardScrollView.Animation.NAVIGATION);
}
protected void onPostExecute(Boolean result) {
if (result) {
playAudio(Sounds.SUCCESS);
} else {
playAudio(Sounds.DISALLOWED);
}
}
};
task.execute(command);
}
}