diff --git a/app/src/main/java/com/odysee/app/MainActivity.java b/app/src/main/java/com/odysee/app/MainActivity.java index 689b353a..4248e735 100644 --- a/app/src/main/java/com/odysee/app/MainActivity.java +++ b/app/src/main/java/com/odysee/app/MainActivity.java @@ -193,6 +193,7 @@ import com.odysee.app.model.lbryinc.Reward; import com.odysee.app.model.lbryinc.RewardVerified; import com.odysee.app.model.lbryinc.Subscription; +import com.odysee.app.supplier.FetchRewardsSupplier; import com.odysee.app.supplier.GetLocalNotificationsSupplier; import com.odysee.app.supplier.NotificationListSupplier; import com.odysee.app.supplier.NotificationUpdateSupplier; @@ -203,7 +204,6 @@ import com.odysee.app.tasks.claim.ClaimListTask; import com.odysee.app.tasks.lbryinc.AndroidPurchaseTask; import com.odysee.app.tasks.lbryinc.ClaimRewardTask; -import com.odysee.app.tasks.lbryinc.FetchRewardsTask; import com.odysee.app.tasks.MergeSubscriptionsTask; import com.odysee.app.tasks.claim.ResolveTask; import com.odysee.app.tasks.lbryinc.NotificationDeleteTask; @@ -744,9 +744,8 @@ public void onClick(View view) { @Override public void onClick(View view) { closeButton.performClick(); -// hideNotifications(); -// openFragment(RewardsFragment.class, true, null); - startActivity(new Intent(view.getContext(), ComingSoon.class)); + hideNotifications(); + openFragment(RewardsFragment.class, true, null); } }); signUserButton.setOnClickListener(new View.OnClickListener() { @@ -1957,7 +1956,6 @@ public void checkAndClaimNewAndroidReward() { Reward.TYPE_NEW_ANDROID, null, null, - this, new ClaimRewardTask.ClaimRewardHandler() { @Override public void onSuccess(double amountClaimed, String message) { @@ -3306,21 +3304,88 @@ protected void onPostExecute(Boolean startupSuccessful) { } private void fetchRewards() { - FetchRewardsTask task = new FetchRewardsTask(null, new FetchRewardsTask.FetchRewardsHandler() { - @Override - public void onSuccess(List rewards) { + String authToken; + AccountManager am = AccountManager.get(this); + Account odyseeAccount = Helper.getOdyseeAccount(am.getAccounts()); + if (odyseeAccount != null) { + authToken = am.peekAuthToken(odyseeAccount, "auth_token_type"); + } else { + authToken = ""; + } + + Map options = new HashMap<>(); + options.put("multiple_rewards_per_type", "true"); + if (odyseeAccount != null && authToken != null) { + options.put("auth_token", authToken); + } + ExecutorService executorService = Executors.newSingleThreadExecutor(); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Supplier> supplier = new FetchRewardsSupplier(options); + CompletableFuture> cf = CompletableFuture.supplyAsync(supplier, executorService); + cf.exceptionally(e -> { + runOnUiThread(new Runnable() { + @Override + public void run() { + showError(e.getMessage()); + } + }); + return null; + }).thenAccept(rewards -> { Lbryio.updateRewardsLists(rewards); if (Lbryio.totalUnclaimedRewardAmount > 0) { updateRewardsUsdValue(); } - } + }); + } else { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + Callable> callable = new Callable>() { + @Override + public List call() { + List rewards = null; + try { + JSONArray results = (JSONArray) Lbryio.parseResponse(Lbryio.call("reward", "list", options, null)); + rewards = new ArrayList<>(); + if (results != null) { + for (int i = 0; i < results.length(); i++) { + rewards.add(Reward.fromJSONObject(results.getJSONObject(i))); + } + } + } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) { + runOnUiThread(new Runnable() { + @Override + public void run() { + showError(ex.getMessage()); + } + }); + } - @Override - public void onError(Exception error) { - } - }); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return rewards; + } + }; + + Future> future = executorService.submit(callable); + + try { + List rewards = future.get(); + + if (rewards != null) { + Lbryio.updateRewardsLists(rewards); + + if (Lbryio.totalUnclaimedRewardAmount > 0) { + updateRewardsUsdValue(); + } + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + }); + t.start(); + } } public void updateRewardsUsdValue() { diff --git a/app/src/main/java/com/odysee/app/adapter/RewardListAdapter.java b/app/src/main/java/com/odysee/app/adapter/RewardListAdapter.java index 82ef7c0f..ef9ef883 100644 --- a/app/src/main/java/com/odysee/app/adapter/RewardListAdapter.java +++ b/app/src/main/java/com/odysee/app/adapter/RewardListAdapter.java @@ -23,7 +23,6 @@ import com.odysee.app.R; import com.odysee.app.model.lbryinc.Reward; import com.odysee.app.utils.Helper; -import com.odysee.app.utils.Lbryio; import com.odysee.app.views.CreditsBalanceView; import lombok.Getter; @@ -84,6 +83,7 @@ private void addCustomReward() { custom.setRewardTitle(context.getString(R.string.custom_reward_title)); custom.setRewardDescription(context.getString(R.string.custom_reward_description)); items.add(custom); + notifyItemInserted(items.indexOf(custom)); } public static class ViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/com/odysee/app/callable/WalletGetUnusedAddress.java b/app/src/main/java/com/odysee/app/callable/WalletGetUnusedAddress.java index 8897881a..8a3f453e 100644 --- a/app/src/main/java/com/odysee/app/callable/WalletGetUnusedAddress.java +++ b/app/src/main/java/com/odysee/app/callable/WalletGetUnusedAddress.java @@ -1,26 +1,22 @@ package com.odysee.app.callable; -import android.accounts.AccountManager; -import android.content.Context; - import com.odysee.app.exceptions.ApiCallException; import com.odysee.app.utils.Lbry; import java.util.concurrent.Callable; public class WalletGetUnusedAddress implements Callable { - Context ctx; + String token; - public WalletGetUnusedAddress(Context ctx) { - this.ctx = ctx; + public WalletGetUnusedAddress(String token) { + this.token = token; } @Override public String call() throws Exception { String address = null; try { - AccountManager am = AccountManager.get(ctx); - address = (String) Lbry.directApiCall(Lbry.METHOD_ADDRESS_UNUSED, am.peekAuthToken(am.getAccounts()[0], "auth_token_type")); + address = (String) Lbry.directApiCall(Lbry.METHOD_ADDRESS_UNUSED, token); } catch (ApiCallException | ClassCastException ex) { ex.printStackTrace(); } diff --git a/app/src/main/java/com/odysee/app/supplier/ClaimRewardSupplier.java b/app/src/main/java/com/odysee/app/supplier/ClaimRewardSupplier.java new file mode 100644 index 00000000..cce607da --- /dev/null +++ b/app/src/main/java/com/odysee/app/supplier/ClaimRewardSupplier.java @@ -0,0 +1,87 @@ +package com.odysee.app.supplier; + +import com.odysee.app.exceptions.ApiCallException; +import com.odysee.app.exceptions.LbryioRequestException; +import com.odysee.app.exceptions.LbryioResponseException; +import com.odysee.app.model.Claim; +import com.odysee.app.model.lbryinc.Reward; +import com.odysee.app.utils.Helper; +import com.odysee.app.utils.Lbry; +import com.odysee.app.utils.Lbryio; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import lombok.SneakyThrows; + +public class ClaimRewardSupplier implements Supplier { + private final String type; + private final String rewardCode; + private final String token; + + public ClaimRewardSupplier(String type, String rewardCode, String token) { + this.type = type; + this.rewardCode = rewardCode; + this.token = token; + } + + @SneakyThrows + @Override + public JSONObject get() { + return claimReward(); + } + + private JSONObject claimReward() throws ApiCallException, LbryioResponseException { + try { + String txid = null; + if (Reward.TYPE_FIRST_CHANNEL.equalsIgnoreCase(type)) { + // fetch a channel + txid = fetchSingleClaimTxid(Claim.TYPE_CHANNEL); + } else if (Reward.TYPE_FIRST_PUBLISH.equalsIgnoreCase(type)) { + // fetch a publish + txid = fetchSingleClaimTxid(Claim.TYPE_STREAM); + } + + Map options = new HashMap<>(); + options.put("reward_type", type); + options.put("wallet_address", (String) Lbry.genericApiCall(Lbry.METHOD_ADDRESS_UNUSED, token)); + + if (!Helper.isNullOrEmpty(rewardCode)) { + options.put("code", rewardCode); + } + if (!Helper.isNullOrEmpty(txid)) { + options.put("transaction_id", txid); + } + if (token != null) { + options.put("auth_token", token); + } + + return (JSONObject) Lbryio.parseResponse(Lbryio.call("reward", "claim", options, Helper.METHOD_POST, null)); + } catch (JSONException | LbryioRequestException ex) { + ex.printStackTrace(); + return null; + } + } + + private String fetchSingleClaimTxid(String claimType) throws ApiCallException, JSONException { + Map options = new HashMap<>(); + options.put("claim_type", claimType); + options.put("page", 1); + options.put("page_size", 1); + options.put("resolve", true); + + JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_CLAIM_LIST, options); + JSONArray items = result.getJSONArray("items"); + if (items.length() > 0) { + Claim claim = Claim.fromJSONObject(items.getJSONObject(0)); + return claim.getTxid(); + } + + return null; + } +} diff --git a/app/src/main/java/com/odysee/app/supplier/FetchRewardsSupplier.java b/app/src/main/java/com/odysee/app/supplier/FetchRewardsSupplier.java new file mode 100644 index 00000000..2f36677a --- /dev/null +++ b/app/src/main/java/com/odysee/app/supplier/FetchRewardsSupplier.java @@ -0,0 +1,39 @@ +package com.odysee.app.supplier; + +import com.odysee.app.exceptions.LbryioRequestException; +import com.odysee.app.exceptions.LbryioResponseException; +import com.odysee.app.model.lbryinc.Reward; +import com.odysee.app.utils.Lbryio; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class FetchRewardsSupplier implements Supplier> { + Map options; + + public FetchRewardsSupplier(Map options) { + this.options = options; + } + + @Override + public List get() { + List rewards = new ArrayList<>(); + try { + JSONArray results = (JSONArray) Lbryio.parseResponse(Lbryio.call("reward", "list", options, null)); + rewards = new ArrayList<>(); + if (results != null) { + for (int i = 0; i < results.length(); i++) { + rewards.add(Reward.fromJSONObject(results.getJSONObject(i))); + } + } + } catch (LbryioResponseException | JSONException | LbryioRequestException e) { + e.printStackTrace(); + } + return rewards; + } +} diff --git a/app/src/main/java/com/odysee/app/tasks/lbryinc/ClaimRewardTask.java b/app/src/main/java/com/odysee/app/tasks/lbryinc/ClaimRewardTask.java index cedb9f8a..1a0424af 100644 --- a/app/src/main/java/com/odysee/app/tasks/lbryinc/ClaimRewardTask.java +++ b/app/src/main/java/com/odysee/app/tasks/lbryinc/ClaimRewardTask.java @@ -1,18 +1,14 @@ package com.odysee.app.tasks.lbryinc; -import android.content.Context; import android.os.AsyncTask; -import android.view.View; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map; -import com.odysee.app.R; import com.odysee.app.exceptions.ApiCallException; import com.odysee.app.exceptions.LbryioRequestException; import com.odysee.app.exceptions.LbryioResponseException; @@ -24,24 +20,21 @@ public class ClaimRewardTask extends AsyncTask { - private final Context context; private final String type; private final String rewardCode; - private final View progressView; private double amountClaimed; + private final String authToken; private final ClaimRewardHandler handler; private Exception error; - public ClaimRewardTask(String type, String rewardCode, View progressView, Context context, ClaimRewardHandler handler) { + public ClaimRewardTask(String type, String rewardCode, String authToken, ClaimRewardHandler handler) { this.type = type; this.rewardCode = rewardCode; - this.progressView = progressView; - this.context = context; + this.authToken = authToken; this.handler = handler; } protected void onPreExecute() { - Helper.setViewVisibility(progressView, View.VISIBLE); } public String doInBackground(Void... params) { @@ -57,7 +50,7 @@ public String doInBackground(Void... params) { } // Get a new wallet address for the reward - String address = (String) Lbry.genericApiCall(Lbry.METHOD_ADDRESS_UNUSED); + String address = (String) Lbry.genericApiCall(Lbry.METHOD_ADDRESS_UNUSED, authToken); Map options = new HashMap<>(); options.put("reward_type", type); options.put("wallet_address", address); @@ -67,16 +60,16 @@ public String doInBackground(Void... params) { if (!Helper.isNullOrEmpty(txid)) { options.put("transaction_id", txid); } + if (authToken != null) { + options.put("auth_token", authToken); + } JSONObject reward = (JSONObject) Lbryio.parseResponse( Lbryio.call("reward", "claim", options, Helper.METHOD_POST, null)); - amountClaimed = Helper.getJSONDouble("reward_amount", 0, reward); - String defaultMessage = context != null ? - context.getResources().getQuantityString( - R.plurals.claim_reward_message, - amountClaimed == 1 ? 1 : 2, - new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)) : ""; - message = Helper.getJSONString("reward_notification", defaultMessage, reward); + if (reward != null) { + amountClaimed = Helper.getJSONDouble("reward_amount", 0, reward); + message = Helper.getJSONString("reward_notification", "", reward); + } } catch (ApiCallException | JSONException | LbryioRequestException | LbryioResponseException ex) { error = ex; } @@ -85,7 +78,6 @@ public String doInBackground(Void... params) { } protected void onPostExecute(String message) { - Helper.setViewVisibility(progressView, View.INVISIBLE); if (handler != null) { if (message != null) { handler.onSuccess(amountClaimed, message); diff --git a/app/src/main/java/com/odysee/app/tasks/lbryinc/FetchRewardsTask.java b/app/src/main/java/com/odysee/app/tasks/lbryinc/FetchRewardsTask.java deleted file mode 100644 index e264fcd0..00000000 --- a/app/src/main/java/com/odysee/app/tasks/lbryinc/FetchRewardsTask.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.odysee.app.tasks.lbryinc; - -import android.os.AsyncTask; -import android.view.View; - -import org.json.JSONArray; -import org.json.JSONException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.odysee.app.exceptions.LbryioRequestException; -import com.odysee.app.exceptions.LbryioResponseException; -import com.odysee.app.model.lbryinc.Reward; -import com.odysee.app.utils.Helper; -import com.odysee.app.utils.Lbryio; - -public class FetchRewardsTask extends AsyncTask> { - private final FetchRewardsHandler handler; - private final View progressView; - private Exception error; - - public FetchRewardsTask(View progressView, FetchRewardsHandler handler) { - this.progressView = progressView; - this.handler = handler; - } - - protected void onPreExecute() { - Helper.setViewVisibility(progressView, View.VISIBLE); - } - - protected List doInBackground(Void... params) { - List rewards = null; - try { - Map options = new HashMap<>(); - options.put("multiple_rewards_per_type", "true"); - JSONArray results = (JSONArray) Lbryio.parseResponse(Lbryio.call("reward", "list", options, null)); - rewards = new ArrayList<>(); - for (int i = 0; i < results.length(); i++) { - rewards.add(Reward.fromJSONObject(results.getJSONObject(i))); - } - } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) { - error = ex; - } - - return rewards; - } - - protected void onPostExecute(List rewards) { - Helper.setViewVisibility(progressView, View.GONE); - if (handler != null) { - if (rewards != null) { - handler.onSuccess(rewards); - } else { - handler.onError(error); - } - } - } - - public interface FetchRewardsHandler { - void onSuccess(List rewards); - void onError(Exception error); - } -} diff --git a/app/src/main/java/com/odysee/app/tasks/lbryinc/LogFileViewTask.java b/app/src/main/java/com/odysee/app/tasks/lbryinc/LogFileViewTask.java deleted file mode 100644 index d93650ac..00000000 --- a/app/src/main/java/com/odysee/app/tasks/lbryinc/LogFileViewTask.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.odysee.app.tasks.lbryinc; - -import android.os.AsyncTask; - -import java.util.HashMap; -import java.util.Map; - -import com.odysee.app.exceptions.LbryioRequestException; -import com.odysee.app.exceptions.LbryioResponseException; -import com.odysee.app.model.Claim; -import com.odysee.app.tasks.GenericTaskHandler; -import com.odysee.app.utils.Lbryio; - -public class LogFileViewTask extends AsyncTask { - private final String uri; - private final Claim claim; - private Exception error; - private final GenericTaskHandler handler; - private final long timeToStart; - - public LogFileViewTask(String uri, Claim claim, long timeToStart, GenericTaskHandler handler) { - this.uri = uri; - this.claim = claim; - this.timeToStart = timeToStart; - this.handler = handler; - } - protected Boolean doInBackground(Void... params) { - try { - Map options = new HashMap<>(); - options.put("uri", uri); - options.put("claim_id", claim.getClaimId()); - options.put("outpoint", String.format("%s:%d", claim.getTxid(), claim.getNout())); - if (timeToStart > 0) { - options.put("time_to_start", String.valueOf(timeToStart)); - } - Lbryio.call("file", "view", options, null).close(); - } catch (LbryioRequestException | LbryioResponseException ex) { - error = ex; - return false; - } - return true; - } - protected void onPostExecute(Boolean result) { - if (handler != null) { - if (result) { - handler.onSuccess(); - } else { - handler.onError(error); - } - } - } -} diff --git a/app/src/main/java/com/odysee/app/ui/findcontent/FileViewFragment.java b/app/src/main/java/com/odysee/app/ui/findcontent/FileViewFragment.java index fe4220e2..f424422a 100644 --- a/app/src/main/java/com/odysee/app/ui/findcontent/FileViewFragment.java +++ b/app/src/main/java/com/odysee/app/ui/findcontent/FileViewFragment.java @@ -179,7 +179,6 @@ import com.odysee.app.tasks.lbryinc.ChannelSubscribeTask; import com.odysee.app.tasks.lbryinc.ClaimRewardTask; import com.odysee.app.tasks.lbryinc.FetchStatCountTask; -import com.odysee.app.tasks.lbryinc.LogFileViewTask; import com.odysee.app.ui.BaseFragment; import com.odysee.app.ui.controls.SolidIconView; import com.odysee.app.utils.Comments; @@ -3134,19 +3133,84 @@ private void logPlay(String url, long startTimeMillis) { private void logFileView(String url, long timeToStart) { if (claim != null) { - LogFileViewTask task = new LogFileViewTask(url, claim, timeToStart, new GenericTaskHandler() { - @Override - public void beforeStart() { } + AccountManager am = AccountManager.get(getContext()); + String authToken = am.peekAuthToken(Helper.getOdyseeAccount(am.getAccounts()), "auth_token_type"); + Map options = new HashMap<>(); + options.put("uri", claim.getPermanentUrl()); + options.put("claim_id", claim.getClaimId()); + options.put("outpoint", String.format("%s:%d", claim.getTxid(), claim.getNout())); + if (timeToStart > 0) { + options.put("time_to_start", String.valueOf(timeToStart)); + } + if (authToken != null) { + options.put("auth_token", authToken); + } + Activity activity = getActivity(); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - @Override - public void onSuccess() { - claimEligibleRewards(); - } + Supplier s = new Supplier() { + @Override + public Boolean get() { + try { + Lbryio.call("file", "view", options, null).close(); + return true; + } catch (LbryioRequestException | LbryioResponseException ex) { + return false; + } + } + }; - @Override - public void onError(Exception error) { } - }); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + CompletableFuture cf = CompletableFuture.supplyAsync(s); + cf.whenComplete((result, ex) -> { + if (result) { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + claimEligibleRewards(); + } + }); + } + if (ex != null) { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + ((MainActivity) activity).showError(ex.getMessage()); + } + }); + } + } + } + }); + } else { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + Lbryio.call("file", "view", options, null).close(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + claimEligibleRewards(); + } + }); + } + } catch (LbryioRequestException | LbryioResponseException ex) { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + ((MainActivity) activity).showError(ex.getMessage()); + } + }); + } + } + } + }); + t.start(); + } } } @@ -3171,9 +3235,11 @@ private void checkIsFollowing() { private void claimEligibleRewards() { // attempt to claim eligible rewards after viewing or playing a file (fail silently) - Context context = getContext(); - ClaimRewardTask firstStreamTask = new ClaimRewardTask(Reward.TYPE_FIRST_STREAM, null, null, context, eligibleRewardHandler); - ClaimRewardTask dailyViewTask = new ClaimRewardTask(Reward.TYPE_DAILY_VIEW, null, null, context, eligibleRewardHandler); + final AccountManager am = AccountManager.get(getContext()); + final String authToken = am.peekAuthToken(Helper.getOdyseeAccount(am.getAccounts()), "auth_token_type"); + + ClaimRewardTask firstStreamTask = new ClaimRewardTask(Reward.TYPE_FIRST_STREAM, null, authToken, eligibleRewardHandler); + ClaimRewardTask dailyViewTask = new ClaimRewardTask(Reward.TYPE_DAILY_VIEW, null, authToken, eligibleRewardHandler); firstStreamTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); dailyViewTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @@ -3187,9 +3253,10 @@ public void onSuccess(double amountClaimed, String message) { amountClaimed == 1 ? 1 : 2, new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)); } - Context context = getContext(); - if (context instanceof MainActivity) { - ((MainActivity) context).showMessage(message); + View root = getView(); + + if (root != null) { + Snackbar.make(root, message, Snackbar.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/com/odysee/app/ui/wallet/RewardsFragment.java b/app/src/main/java/com/odysee/app/ui/wallet/RewardsFragment.java index 88db0aa3..bd19342c 100644 --- a/app/src/main/java/com/odysee/app/ui/wallet/RewardsFragment.java +++ b/app/src/main/java/com/odysee/app/ui/wallet/RewardsFragment.java @@ -1,9 +1,13 @@ package com.odysee.app.ui.wallet; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -20,19 +24,36 @@ import com.google.android.material.snackbar.Snackbar; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Supplier; import com.odysee.app.MainActivity; import com.odysee.app.R; import com.odysee.app.adapter.RewardListAdapter; +import com.odysee.app.exceptions.LbryioRequestException; +import com.odysee.app.exceptions.LbryioResponseException; import com.odysee.app.model.lbryinc.Reward; +import com.odysee.app.supplier.ClaimRewardSupplier; +import com.odysee.app.supplier.FetchRewardsSupplier; import com.odysee.app.tasks.lbryinc.ClaimRewardTask; -import com.odysee.app.tasks.lbryinc.FetchRewardsTask; import com.odysee.app.ui.BaseFragment; import com.odysee.app.utils.Helper; import com.odysee.app.utils.LbryAnalytics; import com.odysee.app.utils.Lbryio; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + public class RewardsFragment extends BaseFragment implements RewardListAdapter.RewardClickListener { private boolean rewardClaimInProgress; @@ -42,10 +63,11 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R private RecyclerView rewardList; private TextView linkFilterUnclaimed; private TextView linkFilterAll; + private View root; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.fragment_rewards, container, false); + root = inflater.inflate(R.layout.fragment_rewards, container, false); linkFilterUnclaimed = root.findViewById(R.id.rewards_filter_link_unclaimed); linkFilterAll = root.findViewById(R.id.rewards_filter_link_all); @@ -96,29 +118,124 @@ public void onStop() { private void fetchRewards() { Helper.setViewVisibility(rewardList, View.INVISIBLE); - FetchRewardsTask task = new FetchRewardsTask(rewardsLoading, new FetchRewardsTask.FetchRewardsHandler() { - @Override - public void onSuccess(List rewards) { + rewardsLoading.setVisibility(View.VISIBLE); + + Activity activity = getActivity(); + + String authToken; + AccountManager am = AccountManager.get(getContext()); + Account odyseeAccount = Helper.getOdyseeAccount(am.getAccounts()); + if (odyseeAccount != null) { + authToken = am.peekAuthToken(odyseeAccount, "auth_token_type"); + } else { + authToken = ""; + } + + Map options = new HashMap<>(); + options.put("multiple_rewards_per_type", "true"); + if (odyseeAccount != null && authToken != null) { + options.put("auth_token", authToken); + } + ExecutorService executorService = Executors.newSingleThreadExecutor(); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Supplier> supplier = new FetchRewardsSupplier(options); + CompletableFuture> cf = CompletableFuture.supplyAsync(supplier, executorService); + cf.exceptionally(e -> { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + ((MainActivity) activity).showError(e.getMessage()); + } + }); + } + return null; + }).thenAccept(rewards -> { Lbryio.updateRewardsLists(rewards); - if (adapter == null) { - adapter = new RewardListAdapter(rewards, getContext()); - adapter.setClickListener(RewardsFragment.this); - adapter.setDisplayMode(RewardListAdapter.DISPLAY_MODE_UNCLAIMED); - rewardList.setAdapter(adapter); - } else { - adapter.setRewards(rewards); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + rewardsLoading.setVisibility(View.GONE); + if (adapter == null) { + adapter = new RewardListAdapter(rewards, getContext()); + adapter.setClickListener(RewardsFragment.this); + adapter.setDisplayMode(RewardListAdapter.DISPLAY_MODE_UNCLAIMED); + rewardList.setAdapter(adapter); + } else { + adapter.setRewards(rewards); + } + Helper.setViewVisibility(rewardList, View.VISIBLE); + } + }); } - Helper.setViewVisibility(rewardList, View.VISIBLE); - } + }); + } else { + Thread t = new Thread(new Runnable() { + @Override + public void run() { + Callable> callable = new Callable>() { + @Override + public List call() { + List rewards = null; + try { + JSONArray results = (JSONArray) Lbryio.parseResponse(Lbryio.call("reward", "list", options, null)); + rewards = new ArrayList<>(); + if (results != null) { + for (int i = 0; i < results.length(); i++) { + rewards.add(Reward.fromJSONObject(results.getJSONObject(i))); + } + } + } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + ((MainActivity) activity).showError(ex.getMessage()); + } + }); + } + } - @Override - public void onError(Exception error) { - // pass - Helper.setViewVisibility(rewardList, View.VISIBLE); - } - }); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return rewards; + } + }; + + Future> future = executorService.submit(callable); + + try { + List rewards = future.get(); + + if (rewards != null) { + Lbryio.updateRewardsLists(rewards); + + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + rewardsLoading.setVisibility(View.GONE); + if (adapter == null) { + adapter = new RewardListAdapter(rewards, getContext()); + adapter.setClickListener(RewardsFragment.this); + adapter.setDisplayMode(RewardListAdapter.DISPLAY_MODE_UNCLAIMED); + rewardList.setAdapter(adapter); + } else { + adapter.setRewards(rewards); + } + Helper.setViewVisibility(rewardList, View.VISIBLE); + } + }); + } + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + }); + t.start(); + } } private void initUi() { @@ -166,37 +283,106 @@ private void claimReward(String type, String code, EditText inputClaimCode, Mate rewardClaimInProgress = true; Helper.setViewEnabled(buttonClaim, false); Helper.setViewEnabled(inputClaimCode, false); - ClaimRewardTask task = new ClaimRewardTask(type, code, loadingView, getContext(), new ClaimRewardTask.ClaimRewardHandler() { - @Override - public void onSuccess(double amountClaimed, String message) { - if (Helper.isNullOrEmpty(message)) { - message = getResources().getQuantityString( - R.plurals.claim_reward_message, - amountClaimed == 1 ? 1 : 2, - new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)); + Activity activity = getActivity(); + + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + loadingView.setVisibility(View.VISIBLE); } - View view = getView(); - if (view != null) { - Snackbar.make(view, message, Snackbar.LENGTH_LONG).show(); + }); + } + + final AccountManager am = AccountManager.get(getContext()); + final String authToken = am.peekAuthToken(Helper.getOdyseeAccount(am.getAccounts()), "auth_token_type"); + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Supplier s = new ClaimRewardSupplier(type, code, authToken); + CompletableFuture cf = CompletableFuture.supplyAsync(s); + cf.whenComplete((result, e) -> { + afterClaimingReward(inputClaimCode, buttonClaim, loadingView, activity, result, e); + }); + } else { + ClaimRewardTask task = new ClaimRewardTask(type, code, authToken, new ClaimRewardTask.ClaimRewardHandler() { + @Override + public void onSuccess(double amountClaimed, String message) { + loadingView.setVisibility(View.INVISIBLE); + if (Helper.isNullOrEmpty(message)) { + message = getResources().getQuantityString( + R.plurals.claim_reward_message, + amountClaimed == 1 ? 1 : 2, + new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)); + } + View view = getView(); + if (view != null) { + Snackbar.make(view, message, Snackbar.LENGTH_LONG).show(); + } + Helper.setViewEnabled(buttonClaim, true); + Helper.setViewEnabled(inputClaimCode, true); + rewardClaimInProgress = false; + + fetchRewards(); } - Helper.setViewEnabled(buttonClaim, true); - Helper.setViewEnabled(inputClaimCode, true); - rewardClaimInProgress = false; - fetchRewards(); - } + @Override + public void onError(Exception error) { + loadingView.setVisibility(View.INVISIBLE); + View view = getView(); + if (view != null && error != null && !Helper.isNullOrEmpty(error.getMessage())) { + Snackbar.make(view, error.getMessage(), Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + } + Helper.setViewEnabled(buttonClaim, true); + Helper.setViewEnabled(inputClaimCode, true); + rewardClaimInProgress = false; + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } - @Override - public void onError(Exception error) { - View view = getView(); - if (view != null && error != null && !Helper.isNullOrEmpty(error.getMessage())) { - Snackbar.make(view, error.getMessage(), Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + private void afterClaimingReward(EditText inputClaimCode, MaterialButton buttonClaim, View loadingView, Activity activity, JSONObject result, Throwable e) { + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + rewardClaimInProgress = false; + loadingView.setVisibility(View.INVISIBLE); + if (e != null) { + String message; + // Exception is wrapped into an CompletionException + Throwable throwable = e.getCause(); + if (throwable != null) { + message = throwable.getMessage(); + } else { + message = e.getMessage(); + } + if (message != null) { + Snackbar.make(root, message, + Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show(); + } + } + if (result != null) { + double amountClaimed = Helper.getJSONDouble("reward_amount", 0, result); + + String defaultMessage = getContext() != null ? + getContext().getResources().getQuantityString( + R.plurals.claim_reward_message, + amountClaimed == 1 ? 1 : 2, + new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)) : ""; + String message = Helper.getJSONString("reward_notification", defaultMessage, result); + + View view = getView(); + if (view != null) { + Snackbar.make(view, message, Snackbar.LENGTH_LONG).show(); + } + Helper.setViewEnabled(buttonClaim, true); + Helper.setViewEnabled(inputClaimCode, true); + + fetchRewards(); + } } - Helper.setViewEnabled(buttonClaim, true); - Helper.setViewEnabled(inputClaimCode, true); - rewardClaimInProgress = false; - } - }); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }); + } } } diff --git a/app/src/main/java/com/odysee/app/ui/wallet/WalletFragment.java b/app/src/main/java/com/odysee/app/ui/wallet/WalletFragment.java index 2a543a8a..f6bf338b 100644 --- a/app/src/main/java/com/odysee/app/ui/wallet/WalletFragment.java +++ b/app/src/main/java/com/odysee/app/ui/wallet/WalletFragment.java @@ -635,8 +635,11 @@ public void generateNewAddress() { Thread thread = new Thread(new Runnable() { @Override public void run() { + AccountManager am = AccountManager.get(getContext()); + String authToken = am.peekAuthToken(am.getAccounts()[0], "auth_token_type"); + ExecutorService executor = Executors.newSingleThreadExecutor(); - Callable callable = new WalletGetUnusedAddress(getContext()); + Callable callable = new WalletGetUnusedAddress(authToken); Future future = executor.submit(callable); try { String addr = future.get();