From f186bbe7b98ffeed99535fef366931400d6f1eae Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 25 Jan 2018 18:04:26 -0500 Subject: [PATCH 001/202] Adding a getter to the Transaction's blockData attribute --- .../main/java/cy/agorise/graphenej/Transaction.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java b/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java index acf5412..e4fceed 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/Transaction.java @@ -100,13 +100,21 @@ public class Transaction implements ByteSerializable, JsonSerializable { } /** - * Updates the block data + * Block data getter * @param blockData New block data */ public void setBlockData(BlockData blockData){ this.blockData = blockData; } + /** + * Block data setter + * @return BlockData instance + */ + public BlockData getBlockData(){ + return this.blockData; + } + /** * Updates the fees for all operations in this transaction. * @param fees: New fees to apply From 2da04adfea09475fb66f4718ff577c2ceb9999a1 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 1 Feb 2018 12:02:03 -0500 Subject: [PATCH 002/202] Enabling and disabling log printing in the SubscriptionMessagesHub --- .../graphenej/api/SubscriptionMessagesHub.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java index 1509be3..b6e3b43 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java @@ -61,6 +61,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs private int subscriptionCounter = 0; private HashMap mHandlerMap = new HashMap<>(); private List pendingHandlerList = new ArrayList<>(); + private boolean printLogs; // State variables private boolean isUnsubscribing; @@ -141,7 +142,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs @Override public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception { String message = frame.getPayloadText(); - System.out.println("<< "+message); + if(printLogs) System.out.println("<< "+message); if(currentId == LOGIN_ID){ currentId = GET_DATABASE_ID; ArrayList emptyParams = new ArrayList<>(); @@ -316,4 +317,12 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs } } } + + public void setPrintLogs(boolean printLogs){ + this.printLogs = printLogs; + } + + public boolean isPrintLogs(){ + return this.printLogs; + } } From dd87ea1df87d4e6c6422630b70b6f59c2aaf616a Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 15 Feb 2018 23:25:36 -0500 Subject: [PATCH 003/202] Version bump --- gradle.properties | 4 ++-- graphenej/build.gradle | 4 ++-- graphenej/src/main/AndroidManifest.xml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 5bfcab3..5057963 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=0.4.6 -VERSION_CODE=9 +VERSION_NAME=0.4.7-alpha1 +VERSION_CODE=10 GROUP=com.github.bilthon POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain. diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 0d30268..04c2e30 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -21,8 +21,8 @@ android { defaultConfig { minSdkVersion 9 targetSdkVersion 24 - versionCode 9 - versionName "0.4.6" + versionCode 10 + versionName "0.4.7-alpha1" vectorDrawables.useSupportLibrary = true } buildTypes { diff --git a/graphenej/src/main/AndroidManifest.xml b/graphenej/src/main/AndroidManifest.xml index 4df96c3..664deb6 100644 --- a/graphenej/src/main/AndroidManifest.xml +++ b/graphenej/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="10" + android:versionName="0.4.7-alpha1" > \ No newline at end of file From 857d861e4b963b5a59a697114f0bb46d4e40b85e Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Tue, 20 Feb 2018 23:38:47 -0500 Subject: [PATCH 004/202] Fixed a problem with the deserialization of the get_objects api call that ocurred when the requested objects were of type impl_asset_bitasset_data_type --- .../main/java/cy/agorise/graphenej/Price.java | 9 +++ .../cy/agorise/graphenej/api/GetObjects.java | 9 ++- .../agorise/graphenej/models/AssetFeed.java | 77 ++++++++++++++++-- .../graphenej/models/BitAssetData.java | 69 +++++++++++++++- .../graphenej/models/ReportedAssetFeed.java | 78 +++++++++++++++++++ .../agorise/graphenej/api/GetObjectsTest.java | 29 +++++-- 6 files changed, 257 insertions(+), 14 deletions(-) create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/models/ReportedAssetFeed.java diff --git a/graphenej/src/main/java/cy/agorise/graphenej/Price.java b/graphenej/src/main/java/cy/agorise/graphenej/Price.java index f45c56c..f0ea53c 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/Price.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/Price.java @@ -18,4 +18,13 @@ package cy.agorise.graphenej; public class Price { public AssetAmount base; public AssetAmount quote; + + @Override + public String toString() { + return String.format("base:[%s, %s], quote:[%s, %s]", + base.getAsset().getObjectId(), + base.getAmount().toString(), + quote.getAsset().getObjectId(), + quote.getAmount().toString()); + } } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java index ab65206..23ead12 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java @@ -24,7 +24,9 @@ import cy.agorise.graphenej.RPC; import cy.agorise.graphenej.UserAccount; import cy.agorise.graphenej.interfaces.WitnessResponseListener; import cy.agorise.graphenej.models.ApiCall; +import cy.agorise.graphenej.models.AssetFeed; import cy.agorise.graphenej.models.BitAssetData; +import cy.agorise.graphenej.models.ReportedAssetFeed; import cy.agorise.graphenej.models.WitnessResponse; /** @@ -91,6 +93,8 @@ public class GetObjects extends BaseGrapheneHandler { String response = frame.getPayloadText(); GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(AssetFeed.class, new AssetFeed.AssetFeedDeserializer()); + gsonBuilder.registerTypeAdapter(ReportedAssetFeed.class, new ReportedAssetFeed.ReportedAssetFeedDeserializer()); gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer()); gsonBuilder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountFullDeserializer()); @@ -117,8 +121,9 @@ public class GetObjects extends BaseGrapheneHandler { case ASSET_BITASSET_DATA: Type BitAssetDataType = new TypeToken>>(){}.getType(); WitnessResponse> witnessResponse = gsonBuilder.create().fromJson(response, BitAssetDataType); - BitAssetData bitAssetData = witnessResponse.result.get(0); - parsedResult.add(bitAssetData); + for(BitAssetData bitAssetData : witnessResponse.result){ + parsedResult.add(bitAssetData); + } } } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/AssetFeed.java b/graphenej/src/main/java/cy/agorise/graphenej/models/AssetFeed.java index 66eef84..6ad6e49 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/models/AssetFeed.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/AssetFeed.java @@ -1,13 +1,80 @@ package cy.agorise.graphenej.models; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; + import cy.agorise.graphenej.Price; /** - * Created by nelson on 1/9/17. + * Price feed of a given asset. */ public class AssetFeed { - public Price settlement_price; - public long maintenance_collateral_ratio; - public long maximum_short_squeeze_ratio; - public Price core_exchange_rate; + public static final String KEY_SETTLEMENT_PRICE = "settlement_price"; + public static final String KEY_MAINTENANCE_COLLATERAL_RATIO = "maintenance_collateral_ratio"; + public static final String KEY_MAXIMUM_SHORT_SQUEEZE_RATIO = "maximum_short_squeeze_ratio"; + public static final String KEY_CORE_EXCHANGE_RATE = "core_exchange_rate"; + + private Price settlement_price; + private long maintenance_collateral_ratio; + private long maximum_short_squeeze_ratio; + private Price core_exchange_rate; + + public AssetFeed(Price settlementPrice, long maintenanceCollateralRatio, long maximumShortSqueezeRatio, Price coreExchangeRate){ + this.settlement_price = settlementPrice; + this.maintenance_collateral_ratio = maintenanceCollateralRatio; + this.maximum_short_squeeze_ratio = maximumShortSqueezeRatio; + this.core_exchange_rate = coreExchangeRate; + } + + public Price getSettlementPrice() { + return settlement_price; + } + + public void setSettlementPrice(Price settlement_price) { + this.settlement_price = settlement_price; + } + + public long getMaintenanceCollateralRatio() { + return maintenance_collateral_ratio; + } + + public void setMaintenanceCollateralRatio(long maintenance_collateral_ratio) { + this.maintenance_collateral_ratio = maintenance_collateral_ratio; + } + + public long getMaximumShortSqueezeRatio() { + return maximum_short_squeeze_ratio; + } + + public void setMaximumShortSqueezeRatio(long maximum_short_squeeze_ratio) { + this.maximum_short_squeeze_ratio = maximum_short_squeeze_ratio; + } + + public Price getCoreExchangeRate() { + return core_exchange_rate; + } + + public void setCoreExchangeRate(Price core_exchange_rate) { + this.core_exchange_rate = core_exchange_rate; + } + + public static class AssetFeedDeserializer implements JsonDeserializer { + + @Override + public AssetFeed deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + Price settlementPrice = context.deserialize(jsonObject.get(KEY_SETTLEMENT_PRICE).getAsJsonObject(), Price.class); + long collateralRatio = jsonObject.get(KEY_MAINTENANCE_COLLATERAL_RATIO).getAsLong(); + long maximumShortSqueezeRatio = jsonObject.get(KEY_MAXIMUM_SHORT_SQUEEZE_RATIO).getAsLong(); + Price coreExchangeRate = context.deserialize(jsonObject.get(KEY_CORE_EXCHANGE_RATE), Price.class); + AssetFeed assetFeed = new AssetFeed(settlementPrice, collateralRatio, maximumShortSqueezeRatio, coreExchangeRate); + return assetFeed; + } + } + } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java index 1d27222..b8d6322 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java @@ -7,10 +7,11 @@ import cy.agorise.graphenej.Price; * This is the representation of the response from the 'get_objects' call with * a 2.4.x id, which will retrieve a 'impl_asset_bitasset_data_type'. * - * Created by nelson on 1/8/17. */ public class BitAssetData extends GrapheneObject { - public Object[] feeds; + public static final String KEY_CURRENT_FEED = "current_feed"; + + public ReportedAssetFeed[] feeds; public AssetFeed current_feed; public String current_feed_publication_time; public Object options; @@ -22,4 +23,68 @@ public class BitAssetData extends GrapheneObject { public BitAssetData(String id) { super(id); } + + public ReportedAssetFeed[] getFeeds() { + return feeds; + } + + public void setFeeds(ReportedAssetFeed[] feeds) { + this.feeds = feeds; + } + + public AssetFeed getCurrentFeed() { + return current_feed; + } + + public void setCurrentFeed(AssetFeed current_feed) { + this.current_feed = current_feed; + } + + public String getCurrentFeedPublicationTime() { + return current_feed_publication_time; + } + + public void setCurrentFeedPublicationTime(String current_feed_publication_time) { + this.current_feed_publication_time = current_feed_publication_time; + } + + public Object getOptions() { + return options; + } + + public void setOptions(Object options) { + this.options = options; + } + + public long getForceSettledVolume() { + return force_settled_volume; + } + + public void setForceSettledVolume(long force_settled_volume) { + this.force_settled_volume = force_settled_volume; + } + + public boolean isIsPredictionMarket() { + return is_prediction_market; + } + + public void setIsPredictionMarket(boolean is_prediction_market) { + this.is_prediction_market = is_prediction_market; + } + + public Price getSettlementPrice() { + return settlement_price; + } + + public void setSettlementPrice(Price settlementPrice) { + this.settlement_price = settlementPrice; + } + + public long getSettlementFund() { + return settlement_fund; + } + + public void setSettlementFund(long settlementFund) { + this.settlement_fund = settlementFund; + } } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/ReportedAssetFeed.java b/graphenej/src/main/java/cy/agorise/graphenej/models/ReportedAssetFeed.java new file mode 100644 index 0000000..ecde713 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/ReportedAssetFeed.java @@ -0,0 +1,78 @@ +package cy.agorise.graphenej.models; + +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.Util; + +/** + * Witness-provided asset price feed + */ + +public class ReportedAssetFeed { + private UserAccount reporter; + private AssetFeed assetFeed; + private Date reportedDate; + + public ReportedAssetFeed(UserAccount userAccount, Date date, AssetFeed assetFeed){ + this.reporter = userAccount; + this.reportedDate = date; + this.assetFeed = assetFeed; + } + + public UserAccount getReporter() { + return reporter; + } + + public void setReporter(UserAccount reporter) { + this.reporter = reporter; + } + + public AssetFeed getAssetFeed() { + return assetFeed; + } + + public void setAssetFeed(AssetFeed assetFeed) { + this.assetFeed = assetFeed; + } + + public Date getReportedDate() { + return reportedDate; + } + + public void setReportedDate(Date reportedDate) { + this.reportedDate = reportedDate; + } + + public static class ReportedAssetFeedDeserializer implements JsonDeserializer { + + @Override + public ReportedAssetFeed deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray array = json.getAsJsonArray(); + String userId = array.get(0).getAsString(); + JsonArray subArray = (JsonArray) array.get(1); + String dateString = subArray.get(0).getAsString(); + + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); + Date reportDate = null; + try { + reportDate = simpleDateFormat.parse(dateString); + } catch (ParseException e) { + e.printStackTrace(); + } + + AssetFeed assetFeed = context.deserialize(subArray.get(1), AssetFeed.class); + UserAccount userAccount = new UserAccount(userId); + return new ReportedAssetFeed(userAccount, reportDate, assetFeed); + } + } +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java b/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java index 5b76e68..4e5ae54 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java @@ -11,6 +11,7 @@ import java.util.List; import cy.agorise.graphenej.Asset; import cy.agorise.graphenej.GrapheneObject; +import cy.agorise.graphenej.Price; import cy.agorise.graphenej.UserAccount; import cy.agorise.graphenej.interfaces.WitnessResponseListener; import cy.agorise.graphenej.models.BaseResponse; @@ -26,7 +27,7 @@ public class GetObjectsTest extends BaseApiTest{ private final Asset asset = new Asset("1.3.0", "BTS", 5); private final UserAccount account = new UserAccount("1.2.116354"); private final UserAccount bilthon_25 = new UserAccount("1.2.151069"); - private final String bitAssetId = "2.4.13"; + private final String[] bitAssetIds = new String[]{"2.4.21", "2.4.83"}; @Test public void testGetAsset(){ @@ -112,20 +113,38 @@ public class GetObjectsTest extends BaseApiTest{ public void testBitAssetData(){ try{ ArrayList ids = new ArrayList<>(); - ids.add(bitAssetId); + for(String bitAssetId : bitAssetIds){ + ids.add(bitAssetId); + } mWebSocket.addListener(new GetObjects(ids, new WitnessResponseListener() { @Override public void onSuccess(WitnessResponse response) { System.out.println("onSuccess"); - List list = (List) response.result; - BitAssetData bitAssetData = (BitAssetData) list.get(0); - System.out.println("feed time: " + bitAssetData.current_feed_publication_time); + List list = (List) response.result; + BitAssetData bitAssetData1 = list.get(0); + BitAssetData bitAssetData2 = list.get(1); + + Price price1 = bitAssetData1.getCurrentFeed().getSettlementPrice(); + Price price2 = bitAssetData2.getCurrentFeed().getSettlementPrice(); + + System.out.println("Bitasset data 1"); + System.out.println("Price 1: "+price1.toString()); + + System.out.println("Bitasset data 2"); + System.out.println("Price 1: "+price2.toString()); + + synchronized (GetObjectsTest.this){ + GetObjectsTest.this.notifyAll(); + } } @Override public void onError(BaseResponse.Error error) { System.out.println("onError"); + synchronized (GetObjectsTest.this){ + GetObjectsTest.this.notifyAll(); + } } })); From d0f9ddbbb988c497166b8ffeb0934668b6ebb467 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 22 Feb 2018 17:38:20 -0500 Subject: [PATCH 005/202] Fixed a remaining issue with the deserialization of the BitAssetData 'get_object' response --- .gitignore | 2 + gradle.properties | 4 +- graphenej/build.gradle | 6 +- graphenej/src/main/AndroidManifest.xml | 4 +- .../cy/agorise/graphenej/api/GetObjects.java | 14 ++- .../graphenej/models/BitAssetData.java | 94 ++++++++++++++++--- .../cy/agorise/graphenej/models/Options.java | 15 +++ .../agorise/graphenej/api/GetObjectsTest.java | 1 + 8 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/models/Options.java diff --git a/.gitignore b/.gitignore index 0790958..4c2a140 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,5 @@ release.properties graphenej/build local.properties + +sample diff --git a/gradle.properties b/gradle.properties index 5057963..59500a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=0.4.7-alpha1 -VERSION_CODE=10 +VERSION_NAME=0.4.7-alpha3 +VERSION_CODE=12 GROUP=com.github.bilthon POM_DESCRIPTION=A Java library for mobile app Developers; Graphene/Bitshares blockchain. diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 04c2e30..8d86c09 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -1,5 +1,5 @@ group 'cy.agorise' -version '0.4.6' +version '0.4.7-alpha2' apply plugin: 'com.android.library' apply from: 'maven-push.gradle' @@ -21,8 +21,8 @@ android { defaultConfig { minSdkVersion 9 targetSdkVersion 24 - versionCode 10 - versionName "0.4.7-alpha1" + versionCode 12 + versionName "0.4.7-alpha3" vectorDrawables.useSupportLibrary = true } buildTypes { diff --git a/graphenej/src/main/AndroidManifest.xml b/graphenej/src/main/AndroidManifest.xml index 664deb6..7e71960 100644 --- a/graphenej/src/main/AndroidManifest.xml +++ b/graphenej/src/main/AndroidManifest.xml @@ -1,8 +1,6 @@ + package="cy.agorise.graphenej"> \ No newline at end of file diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java index 23ead12..91d83be 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/GetObjects.java @@ -5,12 +5,10 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; import com.neovisionaries.ws.client.WebSocket; import com.neovisionaries.ws.client.WebSocketFrame; import java.io.Serializable; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -93,6 +91,7 @@ public class GetObjects extends BaseGrapheneHandler { String response = frame.getPayloadText(); GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(BitAssetData.class, new BitAssetData.BitAssetDataDeserializer()); gsonBuilder.registerTypeAdapter(AssetFeed.class, new AssetFeed.AssetFeedDeserializer()); gsonBuilder.registerTypeAdapter(ReportedAssetFeed.class, new ReportedAssetFeed.ReportedAssetFeedDeserializer()); gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); @@ -106,7 +105,8 @@ public class GetObjects extends BaseGrapheneHandler { JsonParser parser = new JsonParser(); JsonArray resultArray = parser.parse(response).getAsJsonObject().get(WitnessResponse.KEY_RESULT).getAsJsonArray(); - for(JsonElement element : resultArray){ + for(int i = 0; i < resultArray.size(); i++){ + JsonElement element = resultArray.get(i); String id = element.getAsJsonObject().get(GrapheneObject.KEY_ID).getAsString(); GrapheneObject grapheneObject = new GrapheneObject(id); switch (grapheneObject.getObjectType()){ @@ -119,11 +119,9 @@ public class GetObjects extends BaseGrapheneHandler { parsedResult.add(account); break; case ASSET_BITASSET_DATA: - Type BitAssetDataType = new TypeToken>>(){}.getType(); - WitnessResponse> witnessResponse = gsonBuilder.create().fromJson(response, BitAssetDataType); - for(BitAssetData bitAssetData : witnessResponse.result){ - parsedResult.add(bitAssetData); - } + BitAssetData bitAssetData = gson.fromJson(element, BitAssetData.class); + parsedResult.add(bitAssetData); + break; } } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java index b8d6322..910a23b 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java @@ -1,7 +1,21 @@ package cy.agorise.graphenej.models; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + import cy.agorise.graphenej.GrapheneObject; import cy.agorise.graphenej.Price; +import cy.agorise.graphenej.Util; /** * This is the representation of the response from the 'get_objects' call with @@ -9,16 +23,23 @@ import cy.agorise.graphenej.Price; * */ public class BitAssetData extends GrapheneObject { + public static final String KEY_FEEDS = "feeds"; public static final String KEY_CURRENT_FEED = "current_feed"; + public static final String KEY_CURRENT_FEED_PUBLICATION_TIME = "current_feed_publication_time"; + public static final String KEY_OPERATIONS = "operations"; + public static final String KEY_FORCE_SETTLED_VOLUME = "force_settled_volume"; + public static final String KEY_IS_PREDICTION_MARKET = "is_prediction_market"; + public static final String KEY_SETTLEMENT_PRICE = "settlement_price"; + public static final String KEY_SETTLEMENT_FUND = "settlement_fund"; - public ReportedAssetFeed[] feeds; - public AssetFeed current_feed; - public String current_feed_publication_time; - public Object options; - public long force_settled_volume; - public boolean is_prediction_market; - public Price settlement_price; - public long settlement_fund; + private ReportedAssetFeed[] feeds; + private AssetFeed current_feed; + private Date current_feed_publication_time; + private Options options; + private long force_settled_volume; + private boolean is_prediction_market; + private Price settlement_price; + private long settlement_fund; public BitAssetData(String id) { super(id); @@ -40,19 +61,24 @@ public class BitAssetData extends GrapheneObject { this.current_feed = current_feed; } - public String getCurrentFeedPublicationTime() { + public Date getCurrentFeedPublicationTime() { return current_feed_publication_time; } - public void setCurrentFeedPublicationTime(String current_feed_publication_time) { - this.current_feed_publication_time = current_feed_publication_time; + public void setCurrentFeedPublicationTime(String currentFeedPublicationTime) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); + try { + this.current_feed_publication_time = simpleDateFormat.parse(currentFeedPublicationTime); + } catch (ParseException e) { + e.printStackTrace(); + } } - public Object getOptions() { + public Options getOptions() { return options; } - public void setOptions(Object options) { + public void setOptions(Options options) { this.options = options; } @@ -87,4 +113,46 @@ public class BitAssetData extends GrapheneObject { public void setSettlementFund(long settlementFund) { this.settlement_fund = settlementFund; } + + /** + * Custom deserializer used to instantiate the BitAssetData class from the response of the + * 'get_objects' API call. + */ + public static class BitAssetDataDeserializer implements JsonDeserializer { + + @Override + public BitAssetData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + String id = jsonObject.get(GrapheneObject.KEY_ID).getAsString(); + BitAssetData bitAssetData = new BitAssetData(id); + ArrayList reportedAssetFeeds = new ArrayList<>(); + + JsonArray jsonAssetFeeds = jsonObject.get(KEY_FEEDS).getAsJsonArray(); + for(JsonElement jsonFeed : jsonAssetFeeds){ + ReportedAssetFeed reportedAssetFeed = context.deserialize(jsonFeed, ReportedAssetFeed.class); + reportedAssetFeeds.add(reportedAssetFeed); + } + + // Deserializing attributes + JsonElement jsonCurrentFeed = jsonObject.get(KEY_CURRENT_FEED).getAsJsonObject(); + AssetFeed assetFeed = context.deserialize(jsonCurrentFeed, AssetFeed.class); + String publicationTime = jsonObject.get(KEY_CURRENT_FEED_PUBLICATION_TIME).getAsString(); + Options options = context.deserialize(jsonObject.get(KEY_OPERATIONS), Options.class); + long forceSettledVolume = jsonObject.get(KEY_FORCE_SETTLED_VOLUME).getAsLong(); + boolean isPredictionMarket = jsonObject.get(KEY_IS_PREDICTION_MARKET).getAsBoolean(); + Price settlementPrice = context.deserialize(jsonObject.get(KEY_SETTLEMENT_PRICE), Price.class); + long settlementFund = jsonObject.get(KEY_SETTLEMENT_FUND).getAsLong(); + + // Setting attributes + bitAssetData.setFeeds(reportedAssetFeeds.toArray(new ReportedAssetFeed[reportedAssetFeeds.size()])); + bitAssetData.setCurrentFeed(assetFeed); + bitAssetData.setCurrentFeedPublicationTime(publicationTime); + bitAssetData.setOptions(options); + bitAssetData.setForceSettledVolume(forceSettledVolume); + bitAssetData.setIsPredictionMarket(isPredictionMarket); + bitAssetData.setSettlementPrice(settlementPrice); + bitAssetData.setSettlementFund(settlementFund); + return bitAssetData; + } + } } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/Options.java b/graphenej/src/main/java/cy/agorise/graphenej/models/Options.java new file mode 100644 index 0000000..e9ea3ea --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/Options.java @@ -0,0 +1,15 @@ +package cy.agorise.graphenej.models; + +/** + * Class used to represent the 'options' object returned inside the response obtained after + * querying for an object of type 'asset_bitasset_data' (2.4.x) + */ + +public class Options { + private long feed_lifetime_sec; + private long minimum_feeds; + private long force_settlement_delay_sec; + private long force_settlement_offset_percent; + private long maximum_force_settlement_volume; + private String short_backing_asset; +} diff --git a/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java b/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java index 4e5ae54..439c9d6 100644 --- a/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java +++ b/graphenej/src/test/java/cy/agorise/graphenej/api/GetObjectsTest.java @@ -122,6 +122,7 @@ public class GetObjectsTest extends BaseApiTest{ public void onSuccess(WitnessResponse response) { System.out.println("onSuccess"); List list = (List) response.result; + System.out.println("Response array length: "+list.size()); BitAssetData bitAssetData1 = list.get(0); BitAssetData bitAssetData2 = list.get(1); From 3e92673c7b51c2936babd3d9cb17d687b500a8dc Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 1 Mar 2018 11:07:02 -0500 Subject: [PATCH 006/202] Fixed typo --- .../src/main/java/cy/agorise/graphenej/models/BitAssetData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java index 910a23b..79c7fbf 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/models/BitAssetData.java @@ -90,7 +90,7 @@ public class BitAssetData extends GrapheneObject { this.force_settled_volume = force_settled_volume; } - public boolean isIsPredictionMarket() { + public boolean isPredictionMarket() { return is_prediction_market; } From 436fb7111440daea4a2d39af819bea482fe10da0 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Tue, 20 Feb 2018 17:13:21 -0500 Subject: [PATCH 007/202] Initial tests in order to introduce a centralized broker architecture --- build.gradle | 13 ++ graphenej/build.gradle | 6 + .../graphenej/api/ConnectionStatusUpdate.java | 24 +++ .../agorise/graphenej/api/android/RxBus.java | 36 ++++ .../graphenej/api/bitshares/Nodes.java | 15 ++ sample/.gitignore | 1 + sample/build.gradle | 41 +++++ sample/proguard-rules.pro | 21 +++ .../labs/sample/ExampleInstrumentedTest.java | 26 +++ sample/src/main/AndroidManifest.xml | 26 +++ .../luminiasoft/labs/sample/MainActivity.java | 100 +++++++++++ .../labs/sample/NetworkService.java | 119 ++++++++++++ .../labs/sample/SampleApplication.java | 89 +++++++++ .../labs/sample/SecondActivity.java | 13 ++ .../drawable-v24/ic_launcher_foreground.xml | 34 ++++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++++ sample/src/main/res/layout/activity_main.xml | 47 +++++ .../src/main/res/layout/activity_second.xml | 9 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3056 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5024 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2096 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2858 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4569 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7098 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6464 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 10676 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9250 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 15523 bytes sample/src/main/res/values/colors.xml | 6 + sample/src/main/res/values/strings.xml | 3 + sample/src/main/res/values/styles.xml | 11 ++ .../labs/sample/ExampleUnitTest.java | 17 ++ settings.gradle | 1 + 35 files changed, 838 insertions(+) create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/api/ConnectionStatusUpdate.java create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/api/android/RxBus.java create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java create mode 100644 sample/.gitignore create mode 100644 sample/build.gradle create mode 100644 sample/proguard-rules.pro create mode 100644 sample/src/androidTest/java/com/luminiasoft/labs/sample/ExampleInstrumentedTest.java create mode 100644 sample/src/main/AndroidManifest.xml create mode 100644 sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java create mode 100644 sample/src/main/java/com/luminiasoft/labs/sample/NetworkService.java create mode 100644 sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java create mode 100644 sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java create mode 100644 sample/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 sample/src/main/res/drawable/ic_launcher_background.xml create mode 100644 sample/src/main/res/layout/activity_main.xml create mode 100644 sample/src/main/res/layout/activity_second.xml create mode 100644 sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 sample/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 sample/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 sample/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 sample/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 sample/src/main/res/values/colors.xml create mode 100644 sample/src/main/res/values/strings.xml create mode 100644 sample/src/main/res/values/styles.xml create mode 100644 sample/src/test/java/com/luminiasoft/labs/sample/ExampleUnitTest.java diff --git a/build.gradle b/build.gradle index 676dacc..178a870 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,22 @@ subprojects { mavenCentral() } } +allprojects { + repositories { + mavenCentral() + jcenter() + maven { + url "https://maven.google.com" + } + } +} buildscript { repositories { mavenCentral() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { classpath 'com.android.tools.build:gradle:2.3.0' diff --git a/graphenej/build.gradle b/graphenej/build.gradle index 8d86c09..85c6653 100644 --- a/graphenej/build.gradle +++ b/graphenej/build.gradle @@ -11,6 +11,12 @@ dependencies { compile 'org.bitcoinj:bitcoinj-core:0.14.3' compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0' compile group: "org.tukaani", name: "xz", version: "1.6" + + // Rx dependencies + compile 'io.reactivex.rxjava2:rxandroid:2.0.2' + compile 'io.reactivex.rxjava2:rxjava:2.1.9' + compile 'com.jakewharton.rxrelay2:rxrelay:2.0.0' + compile 'com.squareup.okhttp3:okhttp:3.5.0' } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/ConnectionStatusUpdate.java b/graphenej/src/main/java/cy/agorise/graphenej/api/ConnectionStatusUpdate.java new file mode 100644 index 0000000..b58b1bd --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/ConnectionStatusUpdate.java @@ -0,0 +1,24 @@ +package cy.agorise.graphenej.api; + +/** + * Class used to send connection status updates + */ + +public class ConnectionStatusUpdate { + public final static String CONNECTED = "Connected"; + public final static String DISCONNECTED = "Disconnected"; + + private String connectionStatus; + + public ConnectionStatusUpdate(String status){ + this.connectionStatus = status; + } + + public String getConnectionStatus() { + return connectionStatus; + } + + public void setConnectionStatus(String connectionStatus) { + this.connectionStatus = connectionStatus; + } +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/RxBus.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/RxBus.java new file mode 100644 index 0000000..f246f2d --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/RxBus.java @@ -0,0 +1,36 @@ +package cy.agorise.graphenej.api.android; + +import com.jakewharton.rxrelay2.PublishRelay; +import com.jakewharton.rxrelay2.Relay; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; + +/** + * Explained here: https://blog.kaush.co/2014/12/24/implementing-an-event-bus-with-rxjava-rxbus/ + */ +public class RxBus { + + private static RxBus rxBus; + + public static final RxBus getBusInstance(){ + if(rxBus == null){ + rxBus = new RxBus(); + } + return rxBus; + } + + private final Relay _bus = PublishRelay.create().toSerialized(); + + public void send(Object o) { + _bus.accept(o); + } + + public Flowable asFlowable() { + return _bus.toFlowable(BackpressureStrategy.LATEST); + } + + public boolean hasObservers() { + return _bus.hasObservers(); + } +} \ No newline at end of file diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java b/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java new file mode 100644 index 0000000..8fdc8d2 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/bitshares/Nodes.java @@ -0,0 +1,15 @@ +package cy.agorise.graphenej.api.bitshares; + +/** + * Known public nodes + */ + +public class Nodes { + public static final String[] NODE_URLS = { + "wss://bitshares.nus/ws", + "ws://echo.websocket.org", + "wss://dexnode.net/ws", // Dallas, USA + "wss://bitshares.crypto.fans/ws", // Munich, Germany + "wss://bitshares.openledger.info/ws", // Openledger node + }; +} diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1 @@ +/build diff --git a/sample/build.gradle b/sample/build.gradle new file mode 100644 index 0000000..6364279 --- /dev/null +++ b/sample/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "27.0.0" + + + defaultConfig { + applicationId "com.luminiasoft.labs.sample" + minSdkVersion 14 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + multiDexEnabled true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(':graphenej') + compile 'com.android.support:appcompat-v7:26.1.0' + compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.jakewharton.rxbinding2:rxbinding:2.1.1' + compile 'com.jakewharton:butterknife:8.8.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' + testCompile 'junit:junit:4.12' + androidTestCompile('com.android.support.test.espresso:espresso-core:3.0.1', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:multidex:1.0.1' +} diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/sample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/sample/src/androidTest/java/com/luminiasoft/labs/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/com/luminiasoft/labs/sample/ExampleInstrumentedTest.java new file mode 100644 index 0000000..8fa7174 --- /dev/null +++ b/sample/src/androidTest/java/com/luminiasoft/labs/sample/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.luminiasoft.labs.sample; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.luminiasoft.labs.sample", appContext.getPackageName()); + } +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8762327 --- /dev/null +++ b/sample/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java b/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java new file mode 100644 index 0000000..11b831f --- /dev/null +++ b/sample/src/main/java/com/luminiasoft/labs/sample/MainActivity.java @@ -0,0 +1,100 @@ +package com.luminiasoft.labs.sample; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cy.agorise.graphenej.api.ConnectionStatusUpdate; +import cy.agorise.graphenej.api.android.RxBus; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Consumer; + +public class MainActivity extends AppCompatActivity { + private final String TAG = this.getClass().getName(); + + @BindView(R.id.connection_status) + TextView mConnectionStatus; + + @BindView(R.id.response) + TextView mResponse; + + // In case we want to interact directly with the service + private NetworkService mService; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + + RxBus.getBusInstance() + .asFlowable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Consumer() { + + @Override + public void accept(Object o) throws Exception { + if(o instanceof String){ + Log.d(TAG,"Got message"); + mResponse.setText(mResponse.getText() + ((String)o) + "\n"); + }else if(o instanceof ConnectionStatusUpdate){ + Log.d(TAG,"Got connection update"); + mConnectionStatus.setText(((ConnectionStatusUpdate)o).getConnectionStatus()); + } + } + }); + } + + @OnClick(R.id.send_message) + public void onSendMesage(View v){ + mService.sendMessage("Sample message"); + } + + @OnClick(R.id.next_activity) + public void onNextActivity(View v){ + Intent intent = new Intent(this, SecondActivity.class); + startActivity(intent); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, NetworkService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onPause() { + super.onPause(); + unbindService(mConnection); + } + + /** Defines callbacks for backend binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + Log.d(TAG,"onServiceConnected"); + // We've bound to LocalService, cast the IBinder and get LocalService instance + NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; + mService = binder.getService(); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.d(TAG,"onServiceDisconnected"); + } + }; +} diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/NetworkService.java b/sample/src/main/java/com/luminiasoft/labs/sample/NetworkService.java new file mode 100644 index 0000000..15fa515 --- /dev/null +++ b/sample/src/main/java/com/luminiasoft/labs/sample/NetworkService.java @@ -0,0 +1,119 @@ +package com.luminiasoft.labs.sample; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.annotation.Nullable; +import android.util.Log; + +import cy.agorise.graphenej.api.ConnectionStatusUpdate; +import cy.agorise.graphenej.api.android.RxBus; +import cy.agorise.graphenej.api.bitshares.Nodes; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +/** + * Service in charge of mantaining a connection to the full node. + */ + +public class NetworkService extends Service { + private final String TAG = this.getClass().getName(); + + private static final int NORMAL_CLOSURE_STATUS = 1000; + + private final IBinder mBinder = new LocalBinder(); + + private WebSocket mWebSocket; + + private int mSocketIndex; + + private WebSocketListener mWebSocketListener = new WebSocketListener() { + + @Override + public void onOpen(WebSocket webSocket, Response response) { + super.onOpen(webSocket, response); + mWebSocket = webSocket; + RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.CONNECTED)); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + super.onMessage(webSocket, text); + Log.d(TAG,"onMessage. text: "+text); + RxBus.getBusInstance().send(text); + } + + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + super.onClosed(webSocket, code, reason); + Log.d(TAG,"onClosed"); + RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED)); + } + + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + super.onFailure(webSocket, t, response); + Log.d(TAG,"onFailure. Msg: "+t.getMessage()); + RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED)); + mSocketIndex++; + connect(); + } + }; + + @Override + public void onCreate() { + super.onCreate(); + Log.d(TAG,"onCreate"); + connect(); + } + + private void connect(){ + OkHttpClient client = new OkHttpClient(); + String url = Nodes.NODE_URLS[mSocketIndex % Nodes.NODE_URLS.length]; + Request request = new Request.Builder().url(url).build(); + client.newWebSocket(request, mWebSocketListener); + } + + public void sendMessage(String message){ + if(mWebSocket.send(message)){ + Log.d(TAG,"Message enqueued"); + }else{ + Log.w(TAG,"Message not enqueued"); + } + } + + + @Override + public void onDestroy() { + Log.d(TAG,"onDestroy"); + mWebSocket.close(NORMAL_CLOSURE_STATUS, null); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG,"onStartCommand"); + return super.onStartCommand(intent, flags, startId); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + Log.d(TAG,"onBind"); + return mBinder; + } + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + public NetworkService getService() { + // Return this instance of LocalService so clients can call public methods + return NetworkService.this; + } + } +} diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java b/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java new file mode 100644 index 0000000..2459212 --- /dev/null +++ b/sample/src/main/java/com/luminiasoft/labs/sample/SampleApplication.java @@ -0,0 +1,89 @@ +package com.luminiasoft.labs.sample; + +import android.app.Activity; +import android.app.Application; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; + +/** + * Sample application class + */ + +public class SampleApplication extends Application implements Application.ActivityLifecycleCallbacks { + private final String TAG = this.getClass().getName(); + + /** + * Handler instance used to schedule tasks back to the main thread + */ + private Handler mHandler = new Handler(); + + /** + * Constant used to specify how long will the app wait for another activity to go through its starting life + * cycle events before running the teardownConnectionTask task. + * + * This is used as a means to detect whether or not the user has left the app. + */ + private final int DISCONNECT_DELAY = 1500; + + /** + * Runnable used to schedule a service disconnection once the app is not visible to the user for + * more than DISCONNECT_DELAY milliseconds. + */ + private final Runnable mDisconnectRunnable = new Runnable() { + @Override + public void run() { + Log.d(TAG,"Runing stopService"); + stopService(new Intent(getApplicationContext(), NetworkService.class)); + } + }; + + @Override + public void onCreate() { + super.onCreate(); + Intent intent = new Intent(this, NetworkService.class); + startService(intent); + + /* + * Registering this class as a listener to all activity's callback cycle events, in order to + * better estimate when the user has left the app and it is safe to disconnect the websocket connection + */ + registerActivityLifecycleCallbacks(this); + } + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + + } + + @Override + public void onActivityStarted(Activity activity) { + mHandler.removeCallbacks(mDisconnectRunnable); + } + + @Override + public void onActivityResumed(Activity activity) { + + } + + @Override + public void onActivityPaused(Activity activity) { + mHandler.postDelayed(mDisconnectRunnable, DISCONNECT_DELAY); + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } +} diff --git a/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java b/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java new file mode 100644 index 0000000..e16489b --- /dev/null +++ b/sample/src/main/java/com/luminiasoft/labs/sample/SecondActivity.java @@ -0,0 +1,13 @@ +package com.luminiasoft.labs.sample; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; + +public class SecondActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_second); + } +} diff --git a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/sample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..6a55457 --- /dev/null +++ b/sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,47 @@ + + +