|
|
@ -19,66 +19,111 @@ import android.widget.AdapterView; |
|
|
|
|
|
|
|
|
|
|
|
import org.conscrypt.Conscrypt; |
|
|
|
import org.conscrypt.Conscrypt; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.net.URL; |
|
|
|
import java.net.URL; |
|
|
|
import java.security.Security; |
|
|
|
import java.security.Security; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Calendar; |
|
|
|
import java.util.Calendar; |
|
|
|
|
|
|
|
import java.util.Date; |
|
|
|
|
|
|
|
|
|
|
|
import javax.net.ssl.SSLException; |
|
|
|
import javax.net.ssl.SSLException; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import okhttp3.MediaType; |
|
|
|
import okhttp3.OkHttpClient; |
|
|
|
import okhttp3.OkHttpClient; |
|
|
|
import okhttp3.Request; |
|
|
|
import okhttp3.Request; |
|
|
|
|
|
|
|
import okhttp3.RequestBody; |
|
|
|
import okhttp3.Response; |
|
|
|
import okhttp3.Response; |
|
|
|
|
|
|
|
import okio.BufferedSink; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class MainActivity extends Activity { |
|
|
|
public class MainActivity extends Activity { |
|
|
|
|
|
|
|
|
|
|
|
private CardScrollView cardScroller; |
|
|
|
private CardScrollView cardScroller; |
|
|
|
|
|
|
|
|
|
|
|
private View view; |
|
|
|
|
|
|
|
private TeslaResponse vehicleData; |
|
|
|
private TeslaResponse vehicleData; |
|
|
|
private SharedPreferences prefs; |
|
|
|
private SharedPreferences prefs; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ArrayList<View> scrollerViews = new ArrayList<View>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected void onCreate(Bundle bundle) { |
|
|
|
protected void onCreate(Bundle bundle) { |
|
|
|
super.onCreate(bundle); |
|
|
|
super.onCreate(bundle); |
|
|
|
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); |
|
|
|
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); |
|
|
|
StrictMode.setThreadPolicy(policy); |
|
|
|
StrictMode.setThreadPolicy(policy); |
|
|
|
prefs = getPreferences(MODE_PRIVATE); |
|
|
|
prefs = getPreferences(MODE_PRIVATE); |
|
|
|
view = buildStatusView(); |
|
|
|
scrollerViews.add(buildStatusView()); |
|
|
|
view.setKeepScreenOn(true); |
|
|
|
scrollerViews.add(buildLocksView()); |
|
|
|
|
|
|
|
scrollerViews.add(buildControlView()); |
|
|
|
|
|
|
|
|
|
|
|
cardScroller = new CardScrollView(this); |
|
|
|
cardScroller = new CardScrollView(this); |
|
|
|
cardScroller.setAdapter(new CardScrollAdapter() { |
|
|
|
cardScroller.setAdapter(new CardScrollAdapter() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int getCount() { |
|
|
|
public int getCount() { |
|
|
|
return 1; |
|
|
|
return scrollerViews.size(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public Object getItem(int position) { |
|
|
|
public Object getItem(int position) { |
|
|
|
return view; |
|
|
|
return scrollerViews.get(position); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public View getView(int position, View convertView, ViewGroup parent) { |
|
|
|
public View getView(int position, View convertView, ViewGroup parent) { |
|
|
|
return view; |
|
|
|
return scrollerViews.get(position); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int getPosition(Object item) { |
|
|
|
public int getPosition(Object item) { |
|
|
|
if (view.equals(item)) { |
|
|
|
return scrollerViews.indexOf(item); |
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return AdapterView.INVALID_POSITION; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
cardScroller.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
|
|
|
cardScroller.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (position == 2) { |
|
|
|
|
|
|
|
postCommand("flash_lights"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (position == 1) { |
|
|
|
|
|
|
|
if (vehicleData.vehicle_state.locked) { |
|
|
|
|
|
|
|
postCommand("door_unlock"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
postCommand("door_lock"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
scrollerViews.set(1, buildLocksView()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (position == 0) { |
|
|
|
|
|
|
|
scrollerViews.set(0, buildStatusView()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
|
|
|
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
|
|
|
am.playSoundEffect(Sounds.TAP); |
|
|
|
am.playSoundEffect(Sounds.TAP); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
cardScroller.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { |
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (position == 2) { |
|
|
|
|
|
|
|
return postCommand("honk_horn"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (position == 1) { |
|
|
|
|
|
|
|
return postCommand("set_sentry_mode?on=" + !vehicleData.vehicle_state.sentry_mode); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (position == 0) { |
|
|
|
|
|
|
|
return postCommand("remote_start_drive?password=" + prefs.getString("auth_password", "")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |
|
|
|
|
|
|
|
am.playSoundEffect(Sounds.DISALLOWED); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
setContentView(cardScroller); |
|
|
|
setContentView(cardScroller); |
|
|
|
} |
|
|
|
} |
|
|
@ -95,8 +140,28 @@ public class MainActivity extends Activity { |
|
|
|
super.onPause(); |
|
|
|
super.onPause(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private View buildStatusView() { |
|
|
|
private View buildControlView() { |
|
|
|
if (vehicleData == null) { |
|
|
|
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT); |
|
|
|
|
|
|
|
card.setText("Tap to flash lights, long press to honk."); |
|
|
|
|
|
|
|
return card.getView(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private View buildLocksView() { |
|
|
|
|
|
|
|
updateIfNeeded(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT); |
|
|
|
|
|
|
|
String body = vehicleData.vehicle_state.locked ? "Tap to unlock" : "Tap to lock"; |
|
|
|
|
|
|
|
body += "\nLong press to "; |
|
|
|
|
|
|
|
body += vehicleData.vehicle_state.sentry_mode ? "disable" : "enable"; |
|
|
|
|
|
|
|
body += " Sentry Mode"; |
|
|
|
|
|
|
|
card.setText(body); |
|
|
|
|
|
|
|
return card.getView(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void updateIfNeeded() { |
|
|
|
|
|
|
|
Calendar expire = Calendar.getInstance(); |
|
|
|
|
|
|
|
expire.add(Calendar.MINUTE, -5); |
|
|
|
|
|
|
|
if (vehicleData == null || new Date(vehicleData.vehicle_state.timestamp).after(expire.getTime())) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
getVehicleData(); |
|
|
|
getVehicleData(); |
|
|
|
} catch(Exception e) { |
|
|
|
} catch(Exception e) { |
|
|
@ -105,6 +170,10 @@ public class MainActivity extends Activity { |
|
|
|
System.exit(0); // Shouldn't be reached, but just in case let's kill it dead
|
|
|
|
System.exit(0); // Shouldn't be reached, but just in case let's kill it dead
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private View buildStatusView() { |
|
|
|
|
|
|
|
updateIfNeeded(); |
|
|
|
|
|
|
|
|
|
|
|
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT); |
|
|
|
CardBuilder card = new CardBuilder(this, CardBuilder.Layout.TEXT); |
|
|
|
String chargeDuration = ""; |
|
|
|
String chargeDuration = ""; |
|
|
@ -133,20 +202,49 @@ public class MainActivity extends Activity { |
|
|
|
); |
|
|
|
); |
|
|
|
card.setFootnote(String.format("%s last updated: %s", |
|
|
|
card.setFootnote(String.format("%s last updated: %s", |
|
|
|
vehicleData.display_name, |
|
|
|
vehicleData.display_name, |
|
|
|
new SimpleDateFormat("hh:mm").format(vehicleData.charge_state.timestamp) |
|
|
|
new SimpleDateFormat("hh:mm").format(vehicleData.vehicle_state.timestamp) |
|
|
|
)); |
|
|
|
)); |
|
|
|
|
|
|
|
View v = card.getView(); |
|
|
|
return card.getView(); |
|
|
|
v.setKeepScreenOn(true); |
|
|
|
|
|
|
|
return v; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// /api/1/vehicles/{id}/vehicle_data
|
|
|
|
// /api/1/vehicles/{id}/vehicle_data
|
|
|
|
public void getVehicleData() { |
|
|
|
public void getVehicleData() { |
|
|
|
String vehicleId = prefs.getString("vehicle_id", ""); |
|
|
|
String vehicleId = prefs.getString("vehicle_id", ""); |
|
|
|
String json = getResponseFromJsonURL("https://owner-api.teslamotors.com/api/1/vehicles/" + vehicleId +"/vehicle_data"); |
|
|
|
String json = getResponseFromJsonURL("https://owner-api.teslamotors.com/api/1/vehicles/" |
|
|
|
|
|
|
|
+ vehicleId +"/vehicle_data"); |
|
|
|
Gson gson = new Gson(); |
|
|
|
Gson gson = new Gson(); |
|
|
|
ResponseRoot root = gson.fromJson(json, ResponseRoot.class); |
|
|
|
ResponseRoot root = gson.fromJson(json, ResponseRoot.class); |
|
|
|
vehicleData = root.response; |
|
|
|
vehicleData = root.response; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean postCommand(String command) { |
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return response.code() == 200; |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
|
|
e.printStackTrace(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public String getResponseFromJsonURL(String url) { |
|
|
|
public String getResponseFromJsonURL(String url) { |
|
|
|
String token = prefs.getString("bearer_token", ""); |
|
|
|
String token = prefs.getString("bearer_token", ""); |
|
|
|
|
|
|
|
|
|
|
|