package com.omkarmoghe.pokemap;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.google.protobuf.ByteString;
import com.omkarmoghe.pokemap.map.MapWrapperFragment;
import com.omkarmoghe.pokemap.protobuf.PokemonOuterClass.RequestEnvelop;
import com.omkarmoghe.pokemap.settings.SettingsActivity;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
MapWrapperFragment.LocationRequestListener {
public static final String TAG = "Pokemap";
// magic constants fml
private static final String LOGIN_URL = "https://sso.pokemon.com/sso/login?service=https://sso.pokemon.com/sso/oauth2.0/callbackAuthorize";
private static final String LOGIN_OAUTH = "https://sso.pokemon.com/sso/oauth2.0/accessToken";
private static final String PTC_CLIENT_SECRET = "w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR";
public static final String CLIENT_ID = "mobile-app_pokemon-go";
public static final String REDIRECT_URI = "https://www.nianticlabs.com/pokemongo/error";
// Niantic token
private String token;
// fragments
private MapWrapperFragment mMapWrapperFragment;
// Google api shit
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
// Preferences
SharedPreferences pref;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pref = PreferenceManager.getDefaultSharedPreferences(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
setUpGoogleApiClient();
mMapWrapperFragment = MapWrapperFragment.newInstance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_container, mMapWrapperFragment)
.addToBackStack(null)
.commit();
try {
getToken("", "");
}
catch (IOException e) {
e.printStackTrace();
}
//getPokemon();
}
/**
* This definitely needs to be cleaned up a little but yeah successfully intercepts the OAuth requests and gets the token.
* @param username
* @param password
* @throws IOException
*/
private void getToken(final String username, String password) throws IOException {
// Maximum password length is 15 (sign in page enforces this limit, API does not)
final String trimmedPassword = password.length() > 15 ? password.substring(0, 15) : password;
final OkHttpClient client = new OkHttpClient.Builder()
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
})
.cookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getApplicationContext())))
.followRedirects(false)
.followSslRedirects(false)
.build();
Request initialRequest = new Request.Builder()
.addHeader("User-Agent", "Niantic App")
.url(LOGIN_URL)
.build();
client.newCall(initialRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "fuck :(", e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String body = response.body().string();
try {
JSONObject data = new JSONObject(body);
Log.d(TAG, data.toString());
RequestBody formBody = new FormBody.Builder()
.add("lt", data.getString("lt"))
.add("execution", data.getString("execution"))
.add("_eventId", "submit")
.add("username", username)
.add("password", trimmedPassword)
.build();
Request interceptRedirect = new Request.Builder()
.addHeader("User-Agent", "Niantic App")
.url(LOGIN_URL)
.post(formBody)
.build();
client.newCall(interceptRedirect).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "fuck :(", e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, String.valueOf(response.code())); // should be a 302 (redirect)
Log.d(TAG, response.headers().toString()); // should contain a "Location" header
if(response.code() != 302 || response.header("Location") == null) {
runOnUiThread(
new Runnable() {
@Override
public void run() {
Toast.makeText(getBaseContext(), getString(R.string.toast_credentials), Toast.LENGTH_SHORT).show();
}
}
);
return;
}
String ticket = response.header("Location").split("ticket=")[1];
RequestBody loginForm = new FormBody.Builder()
.add("client_id", CLIENT_ID)
.add("redirect_uri", REDIRECT_URI)
.add("client_secret", PTC_CLIENT_SECRET)
.add("grant_type", "refresh_token")
.add("code", ticket)
.build();
Request loginRequest = new Request.Builder()
.addHeader("User-Agent", "Niantic App")
.url(LOGIN_OAUTH)
.post(loginForm)
.build();
client.newCall(loginRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String rawToken = response.body().string();
String cleanToken = rawToken.replaceAll("&expires.*", "").replaceAll(".*access_token=", "");
Log.d(TAG, cleanToken); // success!
token = cleanToken;
}
});
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
/**
* Needs to be translated from the Python library.
* See https://github.com/AHAAAAAAA/PokemonGo-Map
* See https://github.com/AHAAAAAAA/PokemonGo-Map/blob/master/example.py#L378
*/
private void getPokemon() {
try {
RequestEnvelop.Requests.Builder m4 = RequestEnvelop.Requests.newBuilder();
RequestEnvelop.MessageSingleInt.Builder msi = RequestEnvelop.MessageSingleInt.newBuilder();
msi.setF1(System.currentTimeMillis());
m4.setMessage(msi.build().toByteString());
RequestEnvelop.Requests.Builder m5 = RequestEnvelop.Requests.newBuilder();
RequestEnvelop.MessageSingleString.Builder mss = RequestEnvelop.MessageSingleString.newBuilder();
mss.setBytes(ByteString.copyFrom("05daf51635c82611d1aac95c0b051d3ec088a930", "UTF-8"));
m5.setMessage(mss.build().toByteString());
// TODO: walk = sorted(getNeighbors())
RequestEnvelop.Requests.Builder m1 = RequestEnvelop.Requests.newBuilder();
m1.setType(106); // magic number;
RequestEnvelop.MessageQuad.Builder mq = RequestEnvelop.MessageQuad.newBuilder();
// TODO: mq.f1 = ''.join(map(encode, walk))
mq.setF2(ByteString.copyFrom("\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", "UTF-8"));
requestLocation();
mq.setLat(((long) mLastLocation.getLatitude()));
mq.setLong(((long) mLastLocation.getLongitude()));
m1.setMessage(mq.build().toByteString());
// TODO: response = get_profile(...)...
} catch (Exception e) {
e.printStackTrace();
}
}
private void login() {
String username = pref.getString(getString(R.string.pref_username), "");
String password = pref.getString(getString(R.string.pref_password), "");
Log.d(TAG, "Username: " + username);
try {
getToken(username, password);
}
catch (IOException e) {
e.printStackTrace();
}
}
private void setUpGoogleApiClient() {
if (mGoogleApiClient == null) mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
} else if (id == R.id.action_relogin) {
login();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
@Override
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
/// GOOGLE FUSED LOCATION API CALLBACKS ///
@Override
public void onConnected(@Nullable Bundle bundle) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
}
/// INTERFACES ///
@Override
public Location requestLocation() {
if (mLastLocation == null) {
setUpGoogleApiClient();
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
return mLastLocation;
}
}