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
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); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|