Search in sources :

Example 56 with Claim

use of com.odysee.app.model.Claim in project odysee-android by OdyseeTeam.

the class SearchFragment method search.

public void search(String query, int from) {
    boolean queryChanged = checkQuery(query);
    if (query.equals("")) {
        return;
    }
    if (!queryChanged && from > 0) {
        currentFrom = from;
    }
    if (queryChanged) {
        logSearch(query);
    }
    searchLoading = true;
    Context context = getContext();
    boolean canShowMatureContent = false;
    if (context != null) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
        canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
    }
    // modify the request so it returns channels on top
    if (currentQuery != null) {
        final String[] split = currentQuery.split(" ");
        if (split.length == 1 && !currentQuery.startsWith("@")) {
            currentQuery = "@".concat(query);
        }
    }
    Activity a = getActivity();
    if (a != null) {
        a.runOnUiThread(new Runnable() {

            @Override
            public void run() {
                loadingView.setVisibility(View.VISIBLE);
            }
        });
    }
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Callable<List<Claim>> c = new LighthouseSearch(currentQuery, PAGE_SIZE, currentFrom, canShowMatureContent, null);
    Thread t = new Thread(new Runnable() {

        @Override
        public void run() {
            Future<List<Claim>> future = executor.submit(c);
            try {
                List<Claim> results = future.get();
                List<Claim> sanitizedClaims = new ArrayList<>(results.size());
                for (Claim item : results) {
                    if (!item.getValueType().equalsIgnoreCase(Claim.TYPE_REPOST)) {
                        sanitizedClaims.add(item);
                    }
                }
                if (a != null) {
                    a.runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            Context context = getContext();
                            if (context != null) {
                                if (resultListAdapter == null) {
                                    resultListAdapter = new ClaimListAdapter(sanitizedClaims, context);
                                    resultListAdapter.setContextGroupId(SEARCH_CONTEXT_GROUP_ID);
                                    resultListAdapter.addFeaturedItem(buildFeaturedItem(query));
                                    resolveFeaturedItem(buildVanityUrl(query));
                                    resultListAdapter.setListener(SearchFragment.this);
                                    if (resultList != null) {
                                        resultList.setAdapter(resultListAdapter);
                                    }
                                } else {
                                    resultList.setVisibility(View.VISIBLE);
                                    resultListAdapter.addItems(sanitizedClaims);
                                }
                                resultListAdapter.filterBlockedChannels(Lbryio.blockedChannels);
                                checkNothingToBeShown();
                            }
                        }
                    });
                }
                // Lighthouse doesn't return "valueType" of the claim, so another request is needed
                // to determine if an item is a playlist and get the items on the playlist.
                List<String> claimIds = new ArrayList<>();
                for (Claim sanitizedClaim : sanitizedClaims) {
                    if (!sanitizedClaim.getValueType().equalsIgnoreCase(Claim.TYPE_CHANNEL)) {
                        claimIds.add(sanitizedClaim.getClaimId());
                    }
                }
                Map<String, Object> claimSearchOptions = new HashMap<>(2);
                claimSearchOptions.put("claim_ids", claimIds);
                claimSearchOptions.put("page_size", claimIds.size());
                Future<List<Claim>> futureSearch = executor.submit(new Search(claimSearchOptions));
                List<Claim> totalResults = futureSearch.get();
                // For each claim returned from Lighthouse, replace it by the one using Search API
                for (int i = 0; i < sanitizedClaims.size(); i++) {
                    if (!Claim.TYPE_CHANNEL.equalsIgnoreCase(sanitizedClaims.get(i).getValueType())) {
                        int finalI = i;
                        Claim found = totalResults.stream().filter(filteredClaim -> {
                            return sanitizedClaims.get(finalI).getClaimId().equalsIgnoreCase(filteredClaim.getClaimId());
                        }).findAny().orElse(null);
                        if (found != null) {
                            sanitizedClaims.set(i, found);
                            if (a != null && resultListAdapter != null) {
                                a.runOnUiThread(new Runnable() {

                                    @Override
                                    public void run() {
                                        if (!found.getValueType().equalsIgnoreCase(Claim.TYPE_REPOST)) {
                                            resultListAdapter.setItem(found.getClaimId(), found);
                                        } else {
                                            resultListAdapter.removeItem(found);
                                        }
                                    }
                                });
                            }
                        }
                    }
                }
                contentHasReachedEnd = results.size() < PAGE_SIZE;
                searchLoading = false;
                if (a != null) {
                    a.runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            loadingView.setVisibility(View.GONE);
                            int itemCount = resultListAdapter == null ? 0 : resultListAdapter.getItemCount();
                            if (itemCount == 0) {
                                filterLink.setVisibility(View.GONE);
                            } else {
                                filterLink.setVisibility(View.VISIBLE);
                            }
                        }
                    });
                }
            } catch (ExecutionException | InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (!executor.isShutdown()) {
                    executor.shutdown();
                }
            }
        }
    });
    t.start();
}
Also used : Context(android.content.Context) Context(android.content.Context) Chip(com.google.android.material.chip.Chip) java.util(java.util) Setter(lombok.Setter) Bundle(android.os.Bundle) ResolveTask(com.odysee.app.tasks.claim.ResolveTask) NonNull(androidx.annotation.NonNull) ClaimListResultHandler(com.odysee.app.tasks.claim.ClaimListResultHandler) Lbry(com.odysee.app.utils.Lbry) ChipGroup(com.google.android.material.chip.ChipGroup) MenuItem(android.view.MenuItem) LighthouseSearch(com.odysee.app.callable.LighthouseSearch) AppCompatSpinner(androidx.appcompat.widget.AppCompatSpinner) JSONException(org.json.JSONException) JSONObject(org.json.JSONObject) MainActivity(com.odysee.app.MainActivity) LbryUri(com.odysee.app.utils.LbryUri) View(android.view.View) Search(com.odysee.app.callable.Search) LbryFile(com.odysee.app.model.LbryFile) RecyclerView(androidx.recyclerview.widget.RecyclerView) ContextCompat(androidx.core.content.ContextCompat) AsyncTask(android.os.AsyncTask) LayoutInflater(android.view.LayoutInflater) java.util.concurrent(java.util.concurrent) Helper(com.odysee.app.utils.Helper) ClaimListAdapter(com.odysee.app.adapter.ClaimListAdapter) Claim(com.odysee.app.model.Claim) ViewGroup(android.view.ViewGroup) Lbryio(com.odysee.app.utils.Lbryio) DownloadActionListener(com.odysee.app.listener.DownloadActionListener) ClaimCacheKey(com.odysee.app.model.ClaimCacheKey) SharedPreferences(android.content.SharedPreferences) android.widget(android.widget) BaseFragment(com.odysee.app.ui.BaseFragment) LbryAnalytics(com.odysee.app.utils.LbryAnalytics) PreferenceManager(androidx.preference.PreferenceManager) LinearLayoutManager(androidx.recyclerview.widget.LinearLayoutManager) Activity(android.app.Activity) R(com.odysee.app.R) SharedPreferences(android.content.SharedPreferences) MainActivity(com.odysee.app.MainActivity) Activity(android.app.Activity) ClaimListAdapter(com.odysee.app.adapter.ClaimListAdapter) LighthouseSearch(com.odysee.app.callable.LighthouseSearch) LighthouseSearch(com.odysee.app.callable.LighthouseSearch) Search(com.odysee.app.callable.Search) Claim(com.odysee.app.model.Claim)

Example 57 with Claim

use of com.odysee.app.model.Claim in project odysee-android by OdyseeTeam.

the class LibraryFragment method resolveMissingChannelNames.

private void resolveMissingChannelNames(List<String> urls) {
    if (urls.size() > 0) {
        ResolveTask task = new ResolveTask(urls, Lbry.API_CONNECTION_STRING, null, new ClaimListResultHandler() {

            @Override
            public void onSuccess(List<Claim> claims) {
                boolean updated = false;
                for (Claim claim : claims) {
                    if (claim.getClaimId() == null) {
                        continue;
                    }
                    if (contentListAdapter != null) {
                        contentListAdapter.updateSigningChannelForClaim(claim);
                        updated = true;
                    }
                }
                if (updated) {
                    contentListAdapter.notifyDataSetChanged();
                }
            }

            @Override
            public void onError(Exception error) {
            }
        });
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }
}
Also used : ClaimListResultHandler(com.odysee.app.tasks.claim.ClaimListResultHandler) Claim(com.odysee.app.model.Claim) JSONException(org.json.JSONException) ApiCallException(com.odysee.app.exceptions.ApiCallException) ResolveTask(com.odysee.app.tasks.claim.ResolveTask)

Example 58 with Claim

use of com.odysee.app.model.Claim in project odysee-android by OdyseeTeam.

the class LibraryFragment method handleDeleteSelectedClaims.

private void handleDeleteSelectedClaims(List<Claim> selectedClaims) {
    List<String> claimIds = new ArrayList<>();
    for (Claim claim : selectedClaims) {
        claimIds.add(claim.getClaimId());
    }
    if (currentFilter == FILTER_DOWNLOADS) {
        new BulkDeleteFilesTask(claimIds).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        Lbry.unsetFilesForCachedClaims(claimIds);
        contentListAdapter.removeItems(selectedClaims);
        if (actionMode != null) {
            actionMode.finish();
        }
        View root = getView();
        if (root != null) {
            String message = getResources().getQuantityString(R.plurals.files_deleted, claimIds.size());
            Snackbar.make(root, message, Snackbar.LENGTH_LONG).show();
        }
    } else if (currentFilter == FILTER_HISTORY) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Activity a = getActivity();
        String message = getResources().getQuantityString(R.plurals.confirm_delete_files, selectedClaims.size());
        AlertDialog.Builder builder;
        if (a != null) {
            builder = new AlertDialog.Builder(a);
            builder.setTitle(R.string.delete_selection).setMessage(message).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    Thread t = new Thread(new Runnable() {

                        @Override
                        public void run() {
                            for (Claim c : selectedClaims) {
                                try {
                                    Runnable r = new DeleteViewHistoryItem(c.getPermanentUrl());
                                    Future<?> f = executorService.submit(r);
                                    f.get();
                                    a.runOnUiThread(new Runnable() {

                                        @Override
                                        public void run() {
                                            contentListAdapter.removeItem(c);
                                        }
                                    });
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                            }
                            a.runOnUiThread(new Runnable() {

                                @Override
                                public void run() {
                                    if (actionMode != null) {
                                        actionMode.finish();
                                    }
                                    if (executorService != null && !executorService.isShutdown()) {
                                        executorService.shutdown();
                                    }
                                }
                            });
                        }
                    });
                    t.start();
                }
            }).setNegativeButton(R.string.no, null);
            builder.show();
        }
    }
}
Also used : DialogInterface(android.content.DialogInterface) ArrayList(java.util.ArrayList) MainActivity(com.odysee.app.MainActivity) Activity(android.app.Activity) View(android.view.View) RecyclerView(androidx.recyclerview.widget.RecyclerView) CardView(androidx.cardview.widget.CardView) TextView(android.widget.TextView) JSONException(org.json.JSONException) ApiCallException(com.odysee.app.exceptions.ApiCallException) MainThread(androidx.annotation.MainThread) DeleteViewHistoryItem(com.odysee.app.runnable.DeleteViewHistoryItem) ExecutorService(java.util.concurrent.ExecutorService) BulkDeleteFilesTask(com.odysee.app.tasks.file.BulkDeleteFilesTask) Claim(com.odysee.app.model.Claim)

Example 59 with Claim

use of com.odysee.app.model.Claim in project odysee-android by OdyseeTeam.

the class LibraryFragment method fetchPurchases.

private void fetchPurchases() {
    contentListLoading = true;
    Helper.setViewVisibility(linkStats, View.GONE);
    Helper.setViewVisibility(layoutListEmpty, View.GONE);
    PurchaseListTask task = new PurchaseListTask(currentPage, PAGE_SIZE, listLoading, new ClaimSearchResultHandler() {

        @Override
        public void onSuccess(List<Claim> claims, boolean hasReachedEnd) {
            listReachedEnd = hasReachedEnd;
            if (contentListAdapter == null) {
                initContentListAdapter(claims);
            } else {
                contentListAdapter.addItems(claims);
            }
            if (contentListAdapter != null && contentList.getAdapter() == null) {
                contentList.setAdapter(contentListAdapter);
            }
            checkListEmpty();
            contentListLoading = false;
        }

        @Override
        public void onError(Exception error) {
            checkStatsLink();
            checkListEmpty();
            contentListLoading = false;
        }
    });
    task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
Also used : ClaimSearchResultHandler(com.odysee.app.tasks.claim.ClaimSearchResultHandler) Claim(com.odysee.app.model.Claim) JSONException(org.json.JSONException) ApiCallException(com.odysee.app.exceptions.ApiCallException) PurchaseListTask(com.odysee.app.tasks.claim.PurchaseListTask)

Example 60 with Claim

use of com.odysee.app.model.Claim in project odysee-android by OdyseeTeam.

the class CreateSupportDialogFragment method onCreateView.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.dialog_create_support, container, false);
    inputAmount = view.findViewById(R.id.create_support_input_amount);
    inlineBalanceContainer = view.findViewById(R.id.create_support_inline_balance_container);
    inlineBalanceValue = view.findViewById(R.id.create_support_inline_balance_value);
    sendProgress = view.findViewById(R.id.create_support_progress);
    cancelLink = view.findViewById(R.id.create_support_cancel_link);
    sendButton = view.findViewById(R.id.create_support_send);
    channelSpinner = view.findViewById(R.id.create_support_channel_spinner);
    switchTip = view.findViewById(R.id.create_support_make_tip_switch);
    progressLoadingChannels = view.findViewById(R.id.create_support_channel_progress);
    inputAmount.setOnFocusChangeListener(new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View view, boolean hasFocus) {
            inputAmount.setHint(hasFocus ? getString(R.string.zero) : "");
            inlineBalanceContainer.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
        }
    });
    inputAmount.addTextChangedListener(new TextWatcher() {

        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            updateSendButtonText();
        }

        @Override
        public void afterTextChanged(Editable editable) {
        }
    });
    updateInfoText();
    updateSendButtonText();
    String channel = null;
    if (Claim.TYPE_CHANNEL.equalsIgnoreCase(claim.getValueType())) {
        channel = claim.getTitleOrName();
    } else if (claim.getSigningChannel() != null) {
        channel = claim.getPublisherTitle();
    }
    TextView titleView = view.findViewById(R.id.create_support_title);
    String tipTitleText = Helper.isNullOrEmpty(channel) ? getString(R.string.send_a_tip) : getString(R.string.send_a_tip_to, channel);
    titleView.setText(tipTitleText);
    switchTip.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
            if (checked) {
                // show tip info
                titleView.setText(tipTitleText);
                updateSendButtonText();
            } else {
                // show support info
                titleView.setText(R.string.support_this_content);
                sendButton.setText(R.string.send_revocable_support);
            }
            updateInfoText();
        }
    });
    sendButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View view) {
            String amountString = Helper.getValue(inputAmount.getText());
            if (Helper.isNullOrEmpty(amountString)) {
                showError(getString(R.string.invalid_amount));
                return;
            }
            BigDecimal amount = new BigDecimal(amountString);
            if (amount.doubleValue() > Lbry.getAvailableBalance()) {
                showError(getString(R.string.insufficient_balance));
                return;
            }
            if (amount.doubleValue() < Helper.MIN_SPEND) {
                showError(getString(R.string.min_spend_required));
                return;
            }
            Claim selectedChannel = (Claim) channelSpinner.getSelectedItem();
            String channelId = !fetchingChannels && selectedChannel != null ? selectedChannel.getClaimId() : null;
            boolean isTip = switchTip.isChecked();
            disableControls();
            AccountManager am = AccountManager.get(getContext());
            String authToken = am.peekAuthToken(Helper.getOdyseeAccount(am.getAccounts()), "auth_token_type");
            Map<String, Object> options = new HashMap<>();
            options.put("blocking", true);
            options.put("claim_id", claim.getClaimId());
            options.put("amount", new DecimalFormat(Helper.SDK_AMOUNT_FORMAT, new DecimalFormatSymbols(Locale.US)).format(amount.doubleValue()));
            options.put("tip", isTip);
            if (!Helper.isNullOrEmpty(channelId)) {
                options.put("channel_id", channelId);
            }
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                Supplier<String> task = new SupportCreateSupplier(options, authToken);
                CompletableFuture<String> cf = CompletableFuture.supplyAsync(task);
                cf.thenAccept(result -> {
                    Activity activity = getActivity();
                    if (result == null) {
                        if (listener != null) {
                            listener.onSupportCreated(amount, isTip);
                        }
                        dismiss();
                    } else {
                        showError(result);
                    }
                    if (activity != null) {
                        enableControls();
                    }
                });
            } else {
                Thread supportingThread = new Thread(new Runnable() {

                    @Override
                    public void run() {
                        Callable<Boolean> callable = () -> {
                            try {
                                Lbry.authenticatedGenericApiCall(Lbry.METHOD_SUPPORT_CREATE, options, authToken);
                            } catch (ApiCallException ex) {
                                ex.printStackTrace();
                                showError(ex.getMessage());
                                return false;
                            }
                            return true;
                        };
                        ExecutorService executorService = Executors.newSingleThreadExecutor();
                        Future<Boolean> future = executorService.submit(callable);
                        try {
                            boolean result = future.get();
                            Activity activity = getActivity();
                            if (result) {
                                if (listener != null) {
                                    listener.onSupportCreated(amount, isTip);
                                }
                                if (activity != null) {
                                    activity.runOnUiThread(new Runnable() {

                                        @Override
                                        public void run() {
                                            dismiss();
                                        }
                                    });
                                }
                            }
                            if (activity != null) {
                                activity.runOnUiThread(new Runnable() {

                                    @Override
                                    public void run() {
                                        enableControls();
                                    }
                                });
                            }
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                    }
                });
                supportingThread.start();
            }
        }
    });
    cancelLink.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View view) {
            dismiss();
        }
    });
    onWalletBalanceUpdated(Lbry.walletBalance);
    updateInfoText();
    inputAmount.setOnFocusChangeListener(new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View view, boolean hasFocus) {
            if (hasFocus) {
                Context context = getContext();
                if (context != null) {
                    ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
                }
            }
        }
    });
    inputAmount.requestFocus();
    return view;
}
Also used : Bundle(android.os.Bundle) ProgressBar(android.widget.ProgressBar) NonNull(androidx.annotation.NonNull) DecimalFormatSymbols(java.text.DecimalFormatSymbols) ClaimListTask(com.odysee.app.tasks.claim.ClaimListTask) LinkMovementMethod(android.text.method.LinkMovementMethod) WalletBalance(com.odysee.app.model.WalletBalance) AppCompatSpinner(androidx.appcompat.widget.AppCompatSpinner) BigDecimal(java.math.BigDecimal) Future(java.util.concurrent.Future) Locale(java.util.Locale) Map(java.util.Map) View(android.view.View) AccountManager(android.accounts.AccountManager) AsyncTask(android.os.AsyncTask) Helper(com.odysee.app.utils.Helper) HtmlCompat(androidx.core.text.HtmlCompat) Claim(com.odysee.app.model.Claim) ViewGroup(android.view.ViewGroup) Executors(java.util.concurrent.Executors) List(java.util.List) TextView(android.widget.TextView) WalletBalanceListener(com.odysee.app.listener.WalletBalanceListener) Snackbar(com.google.android.material.snackbar.Snackbar) TextWatcher(android.text.TextWatcher) Context(android.content.Context) TextInputEditText(com.google.android.material.textfield.TextInputEditText) ClaimListResultHandler(com.odysee.app.tasks.claim.ClaimListResultHandler) Lbry(com.odysee.app.utils.Lbry) Dialog(android.app.Dialog) HashMap(java.util.HashMap) Callable(java.util.concurrent.Callable) CompletableFuture(java.util.concurrent.CompletableFuture) Supplier(java.util.function.Supplier) Editable(android.text.Editable) InputMethodManager(android.view.inputmethod.InputMethodManager) ArrayList(java.util.ArrayList) MaterialButton(com.google.android.material.button.MaterialButton) MainActivity(com.odysee.app.MainActivity) SwitchMaterial(com.google.android.material.switchmaterial.SwitchMaterial) Build(android.os.Build) ExecutorService(java.util.concurrent.ExecutorService) InlineChannelSpinnerAdapter(com.odysee.app.adapter.InlineChannelSpinnerAdapter) CompoundButton(android.widget.CompoundButton) ApiCallException(com.odysee.app.exceptions.ApiCallException) SupportCreateSupplier(com.odysee.app.supplier.SupportCreateSupplier) LayoutInflater(android.view.LayoutInflater) DecimalFormat(java.text.DecimalFormat) Color(android.graphics.Color) ExecutionException(java.util.concurrent.ExecutionException) BottomSheetDialogFragment(com.google.android.material.bottomsheet.BottomSheetDialogFragment) Activity(android.app.Activity) R(com.odysee.app.R) DecimalFormat(java.text.DecimalFormat) MainActivity(com.odysee.app.MainActivity) Activity(android.app.Activity) CompletableFuture(java.util.concurrent.CompletableFuture) TextWatcher(android.text.TextWatcher) Editable(android.text.Editable) TextView(android.widget.TextView) Supplier(java.util.function.Supplier) SupportCreateSupplier(com.odysee.app.supplier.SupportCreateSupplier) ExecutionException(java.util.concurrent.ExecutionException) Context(android.content.Context) SupportCreateSupplier(com.odysee.app.supplier.SupportCreateSupplier) DecimalFormatSymbols(java.text.DecimalFormatSymbols) ApiCallException(com.odysee.app.exceptions.ApiCallException) View(android.view.View) TextView(android.widget.TextView) BigDecimal(java.math.BigDecimal) ExecutorService(java.util.concurrent.ExecutorService) AccountManager(android.accounts.AccountManager) Map(java.util.Map) HashMap(java.util.HashMap) CompoundButton(android.widget.CompoundButton) Claim(com.odysee.app.model.Claim)

Aggregations

Claim (com.odysee.app.model.Claim)133 Context (android.content.Context)51 MainActivity (com.odysee.app.MainActivity)44 JSONException (org.json.JSONException)42 View (android.view.View)41 TextView (android.widget.TextView)37 RecyclerView (androidx.recyclerview.widget.RecyclerView)36 ApiCallException (com.odysee.app.exceptions.ApiCallException)36 ArrayList (java.util.ArrayList)32 ImageView (android.widget.ImageView)31 AdapterView (android.widget.AdapterView)29 NestedScrollView (androidx.core.widget.NestedScrollView)28 ClaimListResultHandler (com.odysee.app.tasks.claim.ClaimListResultHandler)26 JSONObject (org.json.JSONObject)26 ExecutionException (java.util.concurrent.ExecutionException)25 TrackSelectionOverride (com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride)24 LbryUriException (com.odysee.app.exceptions.LbryUriException)24 SolidIconView (com.odysee.app.ui.controls.SolidIconView)24 WebView (android.webkit.WebView)23 PhotoView (com.github.chrisbanes.photoview.PhotoView)23