diff --git a/README.md b/README.md
index d25a2b20..5d56df04 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ Include the API as jar from your own build, or use Maven/Gradle/SBT/Leiningen: h
Mostly everything is accessed through the PokemonGo class in the API package.
-The constructor of PokemonGo class requires a AuthInfo object which can be obtained from GoogleLogin().login or PTCLogin().login, and a OkHttpClient object.
+The constructor of PokemonGo class requires a AuthInfo object which can be obtained from GoogleLogin().login or PtcLogin().login, and a OkHttpClient object.
EG:
```java
@@ -57,11 +57,15 @@ You're running the sample code on the UI thread. Strict mode policy will throw a
This library is meant to be a Java implementation of the API. Google Volley is specific to Android and should not be introduced in this library. However, if you still want to refactor it, you should create it as a separate project.
+ - How can I use Android's native Google sign in with this libary?
+
+You can't. The Google Indentity Platform uses the SHA1 fingerprint and package name to authenticate the caller of all sign in requests. This means that Niantic would need to add your app's SHA1 fingerprint and package name to their Google API Console. If you ever requested a Google Maps API key, you went through the same process. An alternative would be using a WebView to access the web based OAuth flow. This will work with the client ID and secret provided by this library.
+
## Contributing
- Fork it!
- Create your feature branch: `git checkout -b my-new-feature`
- - Commit your changes: `git commit -am 'Usefull information about your new features'`
+ - Commit your changes: `git commit -am 'Useful information about your new features'`
- Push to the branch: `git push origin my-new-feature`
- Submit a pull request on the `Development` branch :D
diff --git a/src/main/java/com/pokegoapi/api/gym/Battle.java b/src/main/java/com/pokegoapi/api/gym/Battle.java
new file mode 100644
index 00000000..18b1a200
--- /dev/null
+++ b/src/main/java/com/pokegoapi/api/gym/Battle.java
@@ -0,0 +1,257 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.pokegoapi.api.gym;
+
+import POGOProtos.Data.Battle.BattleActionOuterClass.BattleAction;
+import POGOProtos.Data.Battle.BattleActionTypeOuterClass;
+import POGOProtos.Data.Battle.BattlePokemonInfoOuterClass.BattlePokemonInfo;
+import POGOProtos.Data.Battle.BattleStateOuterClass;
+import POGOProtos.Data.Battle.BattleStateOuterClass.BattleState;
+import POGOProtos.Data.PokemonDataOuterClass;
+import POGOProtos.Networking.Requests.Messages.AttackGymMessageOuterClass;
+import POGOProtos.Networking.Requests.Messages.AttackGymMessageOuterClass.AttackGymMessage;
+import POGOProtos.Networking.Requests.Messages.StartGymBattleMessageOuterClass;
+import POGOProtos.Networking.Requests.Messages.StartGymBattleMessageOuterClass.StartGymBattleMessage.Builder;
+import POGOProtos.Networking.Requests.RequestTypeOuterClass;
+import POGOProtos.Networking.Requests.RequestTypeOuterClass.RequestType;
+import POGOProtos.Networking.Responses.AttackGymResponseOuterClass;
+import POGOProtos.Networking.Responses.AttackGymResponseOuterClass.AttackGymResponse;
+import POGOProtos.Networking.Responses.StartGymBattleResponseOuterClass.StartGymBattleResponse;
+import POGOProtos.Networking.Responses.StartGymBattleResponseOuterClass.StartGymBattleResponse.Result;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.pokegoapi.api.PokemonGo;
+import com.pokegoapi.api.pokemon.Pokemon;
+import com.pokegoapi.exceptions.LoginFailedException;
+import com.pokegoapi.exceptions.RemoteServerException;
+import com.pokegoapi.main.ServerRequest;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Battle {
+ private Gym gym;
+ private Pokemon[] team;
+ private List bteam;
+ private StartGymBattleResponse battleResponse;
+ private PokemonGo api;
+ private List gymIndex;
+ @Getter
+ private boolean concluded;
+ @Getter
+ private BattleState outcome;
+
+ /**
+ * New battle to track the state of a battle.
+ *
+ */
+ public Battle(PokemonGo api, Pokemon[] team, Gym gym) {
+ this.team = team;
+ this.gym = gym;
+ this.api = api;
+
+ this.bteam = new ArrayList();
+ this.gymIndex = new ArrayList<>();
+
+ for (int i = 0; i < team.length; i++) {
+ bteam.add(this.createBattlePokemon(team[i]));
+ }
+ }
+
+ /**
+ * Start a battle.
+ *
+ * @return Result of the attempt to start
+ */
+ public Result start() throws LoginFailedException, RemoteServerException {
+
+ Builder builder = StartGymBattleMessageOuterClass.StartGymBattleMessage.newBuilder();
+
+ for (int i = 0; i < team.length; i++) {
+ builder.addAttackingPokemonIds(team[i].getId());
+ }
+
+
+ List defenders = gym.getDefendingPokemon();
+ builder.setGymId(gym.getId());
+ builder.setPlayerLongitude(api.getLongitude());
+ builder.setPlayerLatitude(api.getLatitude());
+ builder.setDefendingPokemonId(defenders.get(0).getId()); // may need to be sorted
+
+ ServerRequest serverRequest = new ServerRequest(RequestType.START_GYM_BATTLE, builder.build());
+ api.getRequestHandler().sendServerRequests(serverRequest);
+
+
+ try {
+ battleResponse = StartGymBattleResponse.parseFrom(serverRequest.getData());
+ } catch (InvalidProtocolBufferException e) {
+ throw new RemoteServerException();
+ }
+
+ // need to send blank action
+ this.sendBlankAction();
+
+
+ for (BattleAction action : battleResponse.getBattleLog().getBattleActionsList()) {
+ gymIndex.add(action.getTargetIndex());
+ }
+
+ return battleResponse.getResult();
+ }
+
+
+
+
+ /**
+ * Attack a gym.
+ *
+ * @param times the amount of times to attack
+ * @return Battle
+ */
+ public AttackGymResponse attack(int times) throws LoginFailedException, RemoteServerException {
+
+ ArrayList actions = new ArrayList();
+
+ for (int i = 0; i < times; i++) {
+ BattleAction action = BattleAction
+ .newBuilder()
+ .setType(BattleActionTypeOuterClass.BattleActionType.ACTION_ATTACK)
+ .setActionStartMs(System.currentTimeMillis() + (100 * times))
+ .setDurationMs(500)
+ .setTargetIndex(-1)
+ .build();
+ actions.add(action);
+ }
+
+ AttackGymResponse result = doActions(actions);
+
+
+
+ return result;
+ }
+
+
+ /**
+ * Creates a battle pokemon object to send with the request.
+ *
+ * @Param Pokemon
+ * @return BattlePokemonInfo
+ */
+ private BattlePokemonInfo createBattlePokemon(Pokemon pokemon) {
+ BattlePokemonInfo info = BattlePokemonInfo
+ .newBuilder()
+ .setCurrentEnergy(0)
+ .setCurrentHealth(100)
+ .setPokemonData(pokemon.getDefaultInstanceForType())
+ .build();
+ return info;
+ }
+
+ /**
+ * Get the Pokemondata for the defenders.
+ *
+ * @param index of defender(0 to gym lever)
+ * @return Battle
+ */
+ private PokemonDataOuterClass.PokemonData getDefender(int index) throws LoginFailedException, RemoteServerException {
+ return gym.getGymMembers().get(0).getPokemonData();
+ }
+
+ /**
+ * Get the last action from server.
+ *
+ * @return BattleAction
+ */
+ private BattleAction getLastActionFromServer() {
+ BattleAction action;
+ int actionCount = battleResponse.getBattleLog().getBattleActionsCount();
+ action = battleResponse.getBattleLog().getBattleActions(actionCount - 1);
+ return action;
+ }
+
+ /**
+ * Send blank action, used for polling the state of the battle. (i think).
+ *
+ * @return AttackGymResponse
+ */
+ private AttackGymResponse sendBlankAction() throws LoginFailedException, RemoteServerException {
+ AttackGymMessage message = AttackGymMessage
+ .newBuilder()
+ .setGymId(gym.getId())
+ .setPlayerLatitude(api.getLatitude())
+ .setPlayerLongitude(api.getLongitude())
+ .setBattleId(battleResponse.getBattleId())
+ .build();
+
+ ServerRequest serverRequest = new ServerRequest(RequestType.ATTACK_GYM, message);
+ api.getRequestHandler().sendServerRequests(serverRequest);
+
+
+ try {
+ return AttackGymResponse.parseFrom(serverRequest.getData());
+ } catch (InvalidProtocolBufferException e) {
+ throw new RemoteServerException();
+ }
+ }
+
+
+ /**
+ * Do Actions in battle.
+ *
+ * @param actions list of actions to send in this request
+ * @return AttackGymResponse
+ */
+ private AttackGymResponse doActions(List actions) throws LoginFailedException, RemoteServerException {
+
+
+ AttackGymMessage.Builder message = AttackGymMessage
+ .newBuilder()
+ .setGymId(gym.getId())
+ .setPlayerLatitude(api.getLatitude())
+ .setPlayerLongitude(api.getLongitude())
+ .setBattleId(battleResponse.getBattleId());
+
+ for (BattleAction action : actions) {
+ message.addAttackActions(action);
+ }
+
+
+
+ ServerRequest serverRequest = new ServerRequest(RequestType.ATTACK_GYM, message.build());
+ api.getRequestHandler().sendServerRequests(serverRequest);
+
+
+ try {
+ AttackGymResponse response = AttackGymResponse.parseFrom(serverRequest.getData());
+
+ if (response.getBattleLog().getState() == BattleState.DEFEATED
+ || response.getBattleLog().getState() == BattleState.VICTORY
+ || response.getBattleLog().getState() == BattleState.TIMED_OUT) {
+ concluded = true;
+ }
+
+ outcome = response.getBattleLog().getState();
+
+
+
+ return response;
+ } catch (InvalidProtocolBufferException e) {
+ throw new RemoteServerException();
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/pokegoapi/api/gym/Gym.java b/src/main/java/com/pokegoapi/api/gym/Gym.java
new file mode 100644
index 00000000..9ece4120
--- /dev/null
+++ b/src/main/java/com/pokegoapi/api/gym/Gym.java
@@ -0,0 +1,168 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.pokegoapi.api.gym;
+
+import POGOProtos.Data.Gym.GymMembershipOuterClass.GymMembership;
+import POGOProtos.Data.PokemonDataOuterClass.PokemonData;
+import POGOProtos.Enums.PokemonIdOuterClass;
+import POGOProtos.Enums.TeamColorOuterClass;
+import POGOProtos.Map.Fort.FortDataOuterClass.FortData;
+import POGOProtos.Networking.Requests.Messages.GetGymDetailsMessageOuterClass.GetGymDetailsMessage;
+import POGOProtos.Networking.Requests.Messages.StartGymBattleMessageOuterClass.StartGymBattleMessage;
+import POGOProtos.Networking.Requests.Messages.StartGymBattleMessageOuterClass.StartGymBattleMessage.Builder;
+import POGOProtos.Networking.Requests.RequestTypeOuterClass.RequestType;
+import POGOProtos.Networking.Responses.GetGymDetailsResponseOuterClass.GetGymDetailsResponse;
+import POGOProtos.Networking.Responses.StartGymBattleResponseOuterClass.StartGymBattleResponse;
+import com.google.protobuf.InvalidProtocolBufferException;
+import com.google.protobuf.ProtocolStringList;
+import com.pokegoapi.api.PokemonGo;
+import com.pokegoapi.api.pokemon.Pokemon;
+import com.pokegoapi.exceptions.LoginFailedException;
+import com.pokegoapi.exceptions.RemoteServerException;
+import com.pokegoapi.main.ServerRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Gym {
+ private FortData proto;
+ private GetGymDetailsResponse details;
+ private PokemonGo api;
+
+ /**
+ * Gym object.
+ *
+ */
+ public Gym(PokemonGo api, FortData proto) {
+ this.api = api;
+ this.proto = proto;
+ this.details = null;
+ }
+
+ public String getId() {
+ return proto.getId();
+ }
+
+ public double getLatitude() {
+ return proto.getLatitude();
+ }
+
+ public double getLongitude() {
+ return proto.getLongitude();
+ }
+
+ public boolean getEnabled() {
+ return proto.getEnabled();
+ }
+
+ public TeamColorOuterClass.TeamColor getOwnedByTeam() {
+ return proto.getOwnedByTeam();
+ }
+
+ public PokemonIdOuterClass.PokemonId getGuardPokemonId() {
+ return proto.getGuardPokemonId();
+ }
+
+ public int getGuardPokemonCp() {
+ return proto.getGuardPokemonCp();
+ }
+
+ public boolean getIsInBattle() {
+ return proto.getIsInBattle();
+ }
+
+ public boolean isAttackable() throws LoginFailedException, RemoteServerException {
+ return this.getGymMembers().size() != 0;
+ }
+
+ public Battle battle(Pokemon[] team) {
+ return new Battle(api, team, this);
+ }
+
+
+ private GetGymDetailsResponse details() throws LoginFailedException, RemoteServerException {
+ if (details == null) {
+ GetGymDetailsMessage reqMsg = GetGymDetailsMessage
+ .newBuilder()
+ .setGymId(this.getId())
+ .setGymLatitude(this.getLatitude())
+ .setGymLongitude(this.getLongitude())
+ .setPlayerLatitude(api.getLatitude())
+ .setPlayerLongitude(api.getLongitude())
+ .build();
+
+
+ ServerRequest serverRequest = new ServerRequest(RequestType.GET_GYM_DETAILS, reqMsg);
+ api.getRequestHandler().sendServerRequests(serverRequest);
+
+ try {
+ details = GetGymDetailsResponse.parseFrom(serverRequest.getData());
+ } catch (InvalidProtocolBufferException e) {
+ throw new RemoteServerException();
+ }
+
+ }
+
+ return details;
+ }
+
+ public String getName() throws LoginFailedException, RemoteServerException {
+ return details().getName();
+ }
+
+ public ProtocolStringList getUrlsList() throws LoginFailedException, RemoteServerException {
+ return details().getUrlsList();
+ }
+
+ public GetGymDetailsResponse.Result getResult() throws LoginFailedException, RemoteServerException {
+ return details().getResult();
+ }
+
+ public boolean inRange() throws LoginFailedException, RemoteServerException {
+ GetGymDetailsResponse.Result result = getResult();
+ return ( result != GetGymDetailsResponse.Result.ERROR_NOT_IN_RANGE);
+ }
+
+ public String getDescription() throws LoginFailedException, RemoteServerException {
+ return details().getDescription();
+ }
+
+
+ public List getGymMembers() throws LoginFailedException, RemoteServerException {
+ return details().getGymState().getMembershipsList();
+ }
+
+ /**
+ * Get a list of pokemon defending this gym.
+ *
+ * @return List of pokemon
+ */
+ public List getDefendingPokemon() throws LoginFailedException, RemoteServerException {
+ List data = new ArrayList();
+
+ for (GymMembership gymMember : getGymMembers()) {
+ data.add(gymMember.getPokemonData());
+ }
+
+ return data;
+ }
+
+ protected PokemonGo getApi() {
+ return api;
+ }
+
+
+}
diff --git a/src/main/java/com/pokegoapi/api/inventory/Hatchery.java b/src/main/java/com/pokegoapi/api/inventory/Hatchery.java
index f44c60fc..710f50ea 100644
--- a/src/main/java/com/pokegoapi/api/inventory/Hatchery.java
+++ b/src/main/java/com/pokegoapi/api/inventory/Hatchery.java
@@ -45,6 +45,7 @@ public Hatchery(PokemonGo pgo) {
public void reset(PokemonGo pgo) {
this.instance = pgo;
+ eggs = new HashSet<>();
}
public void addEgg(EggPokemon egg) {
diff --git a/src/main/java/com/pokegoapi/api/inventory/PokeBank.java b/src/main/java/com/pokegoapi/api/inventory/PokeBank.java
index 423f1ea1..e4e736c6 100644
--- a/src/main/java/com/pokegoapi/api/inventory/PokeBank.java
+++ b/src/main/java/com/pokegoapi/api/inventory/PokeBank.java
@@ -39,6 +39,7 @@ public PokeBank(PokemonGo pgo) {
public void reset(PokemonGo pgo) {
this.instance = pgo;
+ pokemons = new ArrayList<>();
}
/**
diff --git a/src/main/java/com/pokegoapi/api/inventory/Pokedex.java b/src/main/java/com/pokegoapi/api/inventory/Pokedex.java
index ca9c8320..bcf23b3a 100644
--- a/src/main/java/com/pokegoapi/api/inventory/Pokedex.java
+++ b/src/main/java/com/pokegoapi/api/inventory/Pokedex.java
@@ -25,7 +25,7 @@
public class Pokedex {
private PokemonGo api;
- private Map pokedexMap = new HashMap();
+ private Map pokedexMap = new HashMap<>();
public Pokedex(PokemonGo pgo) {
reset(pgo);
@@ -33,6 +33,7 @@ public Pokedex(PokemonGo pgo) {
public void reset(PokemonGo pgo) {
this.api = pgo;
+ pokedexMap = new HashMap<>();
}
/**
diff --git a/src/main/java/com/pokegoapi/api/map/Map.java b/src/main/java/com/pokegoapi/api/map/Map.java
index 6145a287..b1cf1109 100644
--- a/src/main/java/com/pokegoapi/api/map/Map.java
+++ b/src/main/java/com/pokegoapi/api/map/Map.java
@@ -39,9 +39,11 @@
import com.annimon.stream.Collectors;
import com.annimon.stream.Stream;
import com.annimon.stream.function.Function;
+import com.annimon.stream.function.Predicate;
import com.google.protobuf.InvalidProtocolBufferException;
import com.pokegoapi.api.PokemonGo;
import com.pokegoapi.api.map.fort.FortDetails;
+import com.pokegoapi.api.gym.Gym;
import com.pokegoapi.api.map.pokemon.CatchablePokemon;
import com.pokegoapi.api.map.pokemon.NearbyPokemon;
import com.pokegoapi.exceptions.LoginFailedException;
@@ -150,6 +152,24 @@ public List getSpawnPoints() throws LoginFailedException, RemoteServerExc
return points;
}
+ /**
+ * Get a list of gyms near the current location.
+ *
+ * @return List of gyms
+ */
+ public List getGyms() throws LoginFailedException, RemoteServerException {
+ List gyms = new ArrayList<>();
+ MapObjects objects = getMapObjects();
+
+ for (FortData fortdata : objects.getGyms()) {
+ gyms.add(new Gym(api, fortdata));
+ }
+
+ return gyms;
+ }
+
+
+
/**
* Returns a list of decimated spawn points at current location.
*
diff --git a/src/main/java/com/pokegoapi/examples/FightGymExample.java b/src/main/java/com/pokegoapi/examples/FightGymExample.java
new file mode 100644
index 00000000..f1abae3b
--- /dev/null
+++ b/src/main/java/com/pokegoapi/examples/FightGymExample.java
@@ -0,0 +1,102 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.pokegoapi.examples;
+
+
+import POGOProtos.Networking.Envelopes.RequestEnvelopeOuterClass;
+import POGOProtos.Networking.Responses.StartGymBattleResponseOuterClass.StartGymBattleResponse.Result;
+import com.pokegoapi.api.PokemonGo;
+import com.pokegoapi.api.gym.Battle;
+import com.pokegoapi.api.gym.Gym;
+import com.pokegoapi.api.pokemon.Pokemon;
+import com.pokegoapi.auth.PtcLogin;
+import com.pokegoapi.exceptions.LoginFailedException;
+import com.pokegoapi.exceptions.RemoteServerException;
+import com.pokegoapi.util.Log;
+import okhttp3.OkHttpClient;
+
+import java.util.List;
+
+public class FightGymExample {
+
+ /**
+ * Catches a pokemon at an area.
+ */
+ public static void main(String[] args) {
+ OkHttpClient http = new OkHttpClient();
+ RequestEnvelopeOuterClass.RequestEnvelope.AuthInfo auth = null;
+ try {
+ auth = new PtcLogin(http).login(ExampleLoginDetails.LOGIN, ExampleLoginDetails.PASSWORD);
+ // or google
+ //auth = new GoogleLogin(http).login("", ""); // currently uses oauth flow so no user or pass needed
+ PokemonGo go = new PokemonGo(auth, http);
+ // set location
+ go.setLocation(-32.011011, 115.932831, 0);
+
+ List pokemons = go.getInventories().getPokebank().getPokemons();
+ Pokemon[] attackers = new Pokemon[6];
+
+ for (int i = 0; i < 6; i++) {
+ attackers[i] = pokemons.get(i);
+ }
+
+
+ for (Gym gym : go.getMap().getGyms()) {
+ if (gym.isAttackable()) {
+ Battle battle = gym.battle(attackers);
+ // start the battle
+ Result result = battle.start();
+
+ if (result == Result.SUCCESS) {
+ // started battle successfully
+
+ // loop while battle is not finished
+ while (!battle.isConcluded()) {
+ System.out.println("attack:" + battle.attack(5));
+ Thread.sleep(500);
+ }
+
+ System.out.println("Battle result:" + battle.getOutcome());
+
+ } else {
+ System.out.println("FAILED:" + result);
+ }
+ }
+
+ }
+
+ } catch (LoginFailedException | RemoteServerException | InterruptedException e) {
+ // failed to login, invalid credentials, auth issue or server issue.
+ Log.e("Main", "Failed to login or server issue: ", e);
+
+ }
+ }
+}