diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index 5d0eb20a..cde2651a 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.Menu; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; @@ -59,6 +60,9 @@ import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.view.GravityCompat; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -95,7 +99,6 @@ import io.lbry.browser.data.DatabaseHelper; import io.lbry.browser.dialog.ContentScopeDialogFragment; import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.listener.CameraPermissionListener; -import io.lbry.browser.listener.DarkThemeChangeListener; import io.lbry.browser.listener.DownloadActionListener; import io.lbry.browser.listener.FetchChannelsListener; import io.lbry.browser.listener.SdkStatusListener; @@ -133,6 +136,7 @@ import io.lbry.browser.ui.following.FileViewFragment; import io.lbry.browser.ui.following.FollowingFragment; import io.lbry.browser.ui.library.LibraryFragment; import io.lbry.browser.ui.other.AboutFragment; +import io.lbry.browser.ui.publish.PublishFormFragment; import io.lbry.browser.ui.publish.PublishFragment; import io.lbry.browser.ui.publish.PublishesFragment; import io.lbry.browser.ui.search.SearchFragment; @@ -164,6 +168,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static CastContext castContext; public static CastPlayer castPlayer; public static Claim nowPlayingClaim; + public static String nowPlayingClaimUrl; public static boolean startingFilePickerActivity = false; public static boolean startingShareActivity = false; public static boolean startingPermissionRequest = false; @@ -340,6 +345,24 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.content_main), new OnApplyWindowInsetsListener() { + @Override + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + ViewCompat.onApplyWindowInsets(findViewById(R.id.url_suggestions_container), + insets.replaceSystemWindowInsets(0, 0, 0, insets.getSystemWindowInsetBottom())); + return ViewCompat.onApplyWindowInsets(v, + insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, + 0, insets.getSystemWindowInsetBottom())); + } + }); + /*ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.url_suggestions_container), new OnApplyWindowInsetsListener() { + @Override + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + return ViewCompat.onApplyWindowInsets(v, + insets.replaceSystemWindowInsets(0, 0,0, insets.getSystemWindowInsetBottom())); + } + });*/ + // register receivers registerRequestsReceiver(); registerServiceActionsReceiver(); @@ -350,10 +373,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public void onSystemUiVisibilityChange(int visibility) { if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { // not fullscreen - View appBarMainContainer = findViewById(R.id.app_bar_main_container); - appBarMainContainer.setPadding( - appBarMainContainer.getPaddingLeft(), appBarMainContainer.getPaddingTop(), appBarMainContainer.getPaddingRight(), 0); - appBarMainContainer.setFitsSystemWindows(true); } } }); @@ -434,8 +453,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener findViewById(R.id.global_now_playing_card).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (nowPlayingClaim != null) { - openFileUrl(!Helper.isNullOrEmpty(nowPlayingClaim.getShortUrl()) ? nowPlayingClaim.getShortUrl() : nowPlayingClaim.getPermanentUrl()); + if (nowPlayingClaim != null && !Helper.isNullOrEmpty(nowPlayingClaimUrl)) { + openFileUrl(nowPlayingClaimUrl); } } }); @@ -622,6 +641,14 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener openFragment(ChannelFormFragment.class, true, NavMenuItem.ID_ITEM_CHANNELS, params); } + public void openPublishForm(Claim claim) { + Map params = new HashMap<>(); + if (claim != null) { + params.put("claim", claim); + } + openFragment(PublishFormFragment.class, true, NavMenuItem.ID_ITEM_NEW_PUBLISH, params); + } + public void openChannelUrl(String url) { Map params = new HashMap<>(); params.put("url", url); @@ -722,6 +749,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener dbHelper.close(); } stopExoplayer(); + nowPlayingClaim = null; + nowPlayingClaimUrl = null; super.onDestroy(); } @@ -1229,9 +1258,11 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } public void exitFullScreenMode() { + View appBarMainContainer = findViewById(R.id.app_bar_main_container); View decorView = getWindow().getDecorView(); int flags = isDarkMode() ? (View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE) : (View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE); + appBarMainContainer.setFitsSystemWindows(true); decorView.setSystemUiVisibility(flags); if (!Lbry.SDK_READY) { @@ -1262,7 +1293,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } else if (!appStarted) { // first run completed, startup startup(); - } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) { + return; + } + + if (getSupportFragmentManager().getBackStackEntryCount() == 0) { openFragment(FollowingFragment.class, false, NavMenuItem.ID_ITEM_FOLLOWING); fetchRewards(); } @@ -1707,16 +1741,22 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } else { boolean handled = false; ChannelFormFragment channelFormFragment = null; + PublishFormFragment publishFormFragment = null; for (Fragment fragment : openNavFragments.values()) { if (fragment instanceof ChannelFormFragment) { channelFormFragment = ((ChannelFormFragment) fragment); break; } + if (fragment instanceof PublishFormFragment) { + publishFormFragment = ((PublishFormFragment) fragment); + break; + } } if (channelFormFragment != null && channelFormFragment.isSaveInProgress()) { handled = true; return; } + //if (publishFormFragment != null && ) if (!handled) { // check fragment and nav history @@ -1975,17 +2015,20 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener subUrls.add(url.toString()); } Lbryio.subscriptions = subscriptions; + startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_LOADED, true); // resolve subscriptions if (subUrls.size() > 0 && Lbryio.cacheResolvedSubscriptions.size() != Lbryio.subscriptions.size()) { List resolvedSubs = Lbry.resolve(subUrls, Lbry.LBRY_TV_CONNECTION_STRING); Lbryio.cacheResolvedSubscriptions = resolvedSubs; } + // if no exceptions occurred here, subscriptions have been loaded and resolved + startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true); + } else { + // user has not subscribed to anything + startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_LOADED, true); + startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true); } - - // if no exceptions occurred here, subscriptions have been loaded and resolved - startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_LOADED, true); - startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true); } else { startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_LOADED, true); startupStages.put(STARTUP_STAGE_SUBSCRIPTIONS_RESOLVED, true); @@ -2004,6 +2047,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (!startupSuccessful) { // show which startup stage failed renderStartupFailed(startupStages); + appStarted = false; return; } @@ -2304,8 +2348,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener return flatMenu; } - public void setNowPlayingClaim(Claim claim) { + public void setNowPlayingClaim(Claim claim, String url) { nowPlayingClaim = claim; + nowPlayingClaimUrl = url; if (claim != null) { ((TextView) findViewById(R.id.global_now_playing_title)).setText(nowPlayingClaim.getTitle()); ((TextView) findViewById(R.id.global_now_playing_channel_title)).setText(nowPlayingClaim.getPublisherTitle()); @@ -2314,6 +2359,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public void clearNowPlayingClaim() { nowPlayingClaim = null; + nowPlayingClaimUrl = null; findViewById(R.id.global_now_playing_card).setVisibility(View.GONE); ((TextView) findViewById(R.id.global_now_playing_title)).setText(null); ((TextView) findViewById(R.id.global_now_playing_channel_title)).setText(null); @@ -2556,16 +2602,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener return (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED); } - public void onThemeChanged() { - FragmentManager manager = getSupportFragmentManager(); - for (int i = 0; i < manager.getFragments().size(); i++) { - Fragment f = manager.getFragments().get(i); - if (f instanceof DarkThemeChangeListener) { - ((DarkThemeChangeListener) f).onDarkThemeToggled(); - } - } - } - public interface BackPressInterceptor { boolean onBackPressed(); } diff --git a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java index b2ac5c4e..cbc4e513 100644 --- a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java @@ -43,6 +43,8 @@ public class ClaimListAdapter extends RecyclerView.Adapter notFoundClaimIdMap; private Map notFoundClaimUrlMap; + @Setter + private boolean hideFee; @Setter private boolean canEnterSelectionMode; private Context context; @@ -421,7 +423,7 @@ public class ClaimListAdapter extends RecyclerView.Adapter 0 ? View.VISIBLE : View.GONE); + vh.feeContainer.setVisibility(cost.doubleValue() > 0 && !hideFee ? View.VISIBLE : View.GONE); vh.feeView.setText(cost.doubleValue() > 0 ? Helper.shortCurrencyFormat(cost.doubleValue()) : "Paid"); vh.alphaView.setText(item.getName().substring(0, Math.min(5, item.getName().length() - 1))); vh.publisherView.setText(signingChannel != null ? signingChannel.getName() : context.getString(R.string.anonymous)); diff --git a/app/src/main/java/io/lbry/browser/dialog/CustomizeTagsDialogFragment.java b/app/src/main/java/io/lbry/browser/dialog/CustomizeTagsDialogFragment.java index 0dd193ed..1a328108 100644 --- a/app/src/main/java/io/lbry/browser/dialog/CustomizeTagsDialogFragment.java +++ b/app/src/main/java/io/lbry/browser/dialog/CustomizeTagsDialogFragment.java @@ -159,7 +159,13 @@ public class CustomizeTagsDialogFragment extends BottomSheetDialogFragment { } private void updateKnownTags(String filter, int limit, boolean clearPrevious) { - UpdateSuggestedTagsTask task = new UpdateSuggestedTagsTask(filter, SUGGESTED_LIMIT, followedTagsAdapter, suggestedTagsAdapter, clearPrevious, new UpdateSuggestedTagsTask.KnownTagsHandler() { + UpdateSuggestedTagsTask task = new UpdateSuggestedTagsTask( + filter, + SUGGESTED_LIMIT, + followedTagsAdapter, + suggestedTagsAdapter, + clearPrevious, + false, new UpdateSuggestedTagsTask.KnownTagsHandler() { @Override public void onSuccess(List tags) { if (suggestedTagsAdapter == null) { diff --git a/app/src/main/java/io/lbry/browser/dialog/RepostClaimDialogFragment.java b/app/src/main/java/io/lbry/browser/dialog/RepostClaimDialogFragment.java index 2443fa9c..f34099ce 100644 --- a/app/src/main/java/io/lbry/browser/dialog/RepostClaimDialogFragment.java +++ b/app/src/main/java/io/lbry/browser/dialog/RepostClaimDialogFragment.java @@ -86,7 +86,7 @@ public class RepostClaimDialogFragment extends BottomSheetDialogFragment impleme textTitle.setText(getString(R.string.repost_title, claim.getTitle())); inputName.setText(claim.getName()); - inputDeposit.setText(R.string.min_repost_deposit); + inputDeposit.setText(R.string.min_deposit); channelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView adapterView, View view, int position, long l) { diff --git a/app/src/main/java/io/lbry/browser/listener/DarkThemeChangeListener.java b/app/src/main/java/io/lbry/browser/listener/DarkThemeChangeListener.java deleted file mode 100644 index 3d7cd886..00000000 --- a/app/src/main/java/io/lbry/browser/listener/DarkThemeChangeListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.lbry.browser.listener; - -public interface DarkThemeChangeListener { - void onDarkThemeToggled(); -} diff --git a/app/src/main/java/io/lbry/browser/model/Language.java b/app/src/main/java/io/lbry/browser/model/Language.java new file mode 100644 index 00000000..e3f4567b --- /dev/null +++ b/app/src/main/java/io/lbry/browser/model/Language.java @@ -0,0 +1,16 @@ +package io.lbry.browser.model; + +import lombok.Data; + +@Data +public class Language { + private String code; + private String name; + private int stringResourceId; + + public Language(String code, String name, int stringResourceId) { + this.code = code; + this.name = name; + this.stringResourceId = stringResourceId; + } +} diff --git a/app/src/main/java/io/lbry/browser/model/License.java b/app/src/main/java/io/lbry/browser/model/License.java new file mode 100644 index 00000000..b278eafa --- /dev/null +++ b/app/src/main/java/io/lbry/browser/model/License.java @@ -0,0 +1,20 @@ +package io.lbry.browser.model; + +import lombok.Data; + +@Data +public class License { + private String name; + private String url; + private int stringResourceId; + + public License(String name, int stringResourceId) { + this.name = name; + this.stringResourceId = stringResourceId; + } + public License(String name, String url, int stringResourceId) { + this.name = name; + this.url = url; + this.stringResourceId = stringResourceId; + } +} diff --git a/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java b/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java index 7ee1c350..96244e23 100644 --- a/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java @@ -48,13 +48,27 @@ public class ChannelCreateUpdateTask extends AsyncTask { options.put("claim_id", claim.getClaimId()); } options.put("bid", new DecimalFormat(Helper.SDK_AMOUNT_FORMAT).format(deposit.doubleValue())); - options.put("title", claim.getTitle()); - options.put("cover_url", claim.getCoverUrl()); - options.put("thumbnail_url", claim.getThumbnailUrl()); - options.put("description", claim.getDescription()); - options.put("website_url", claim.getWebsiteUrl()); - options.put("email", claim.getEmail()); - options.put("tags", claim.getTags()); + if (!Helper.isNullOrEmpty(claim.getTitle())) { + options.put("title", claim.getTitle()); + } + if (!Helper.isNullOrEmpty(claim.getCoverUrl())) { + options.put("cover_url", claim.getCoverUrl()); + } + if (!Helper.isNullOrEmpty(claim.getThumbnailUrl())) { + options.put("thumbnail_url", claim.getThumbnailUrl()); + } + if (!Helper.isNullOrEmpty(claim.getDescription())) { + options.put("description", claim.getDescription()); + } + if (!Helper.isNullOrEmpty(claim.getWebsiteUrl())) { + options.put("website_url", claim.getWebsiteUrl()); + } + if (!Helper.isNullOrEmpty(claim.getEmail())) { + options.put("email", claim.getEmail()); + } + if (claim.getTags() != null && claim.getTags().size() > 0) { + options.put("tags", claim.getTags()); + } options.put("blocking", true); Claim claimResult = null; diff --git a/app/src/main/java/io/lbry/browser/tasks/LighthouseSearchTask.java b/app/src/main/java/io/lbry/browser/tasks/LighthouseSearchTask.java index ae3f9078..6da1d943 100644 --- a/app/src/main/java/io/lbry/browser/tasks/LighthouseSearchTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/LighthouseSearchTask.java @@ -9,7 +9,7 @@ import java.util.List; import io.lbry.browser.exceptions.LbryRequestException; import io.lbry.browser.exceptions.LbryResponseException; import io.lbry.browser.model.Claim; -import io.lbry.browser.tasks.claim.ClaimSearchTask; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lighthouse; @@ -19,11 +19,11 @@ public class LighthouseSearchTask extends AsyncTask> { private int from; private boolean nsfw; private String relatedTo; - private ClaimSearchTask.ClaimSearchResultHandler handler; + private ClaimSearchResultHandler handler; private ProgressBar progressBar; private Exception error; - public LighthouseSearchTask(String rawQuery, int size, int from, boolean nsfw, String relatedTo, ProgressBar progressBar, ClaimSearchTask.ClaimSearchResultHandler handler) { + public LighthouseSearchTask(String rawQuery, int size, int from, boolean nsfw, String relatedTo, ProgressBar progressBar, ClaimSearchResultHandler handler) { this.rawQuery = rawQuery; this.size = size; this.from = from; diff --git a/app/src/main/java/io/lbry/browser/tasks/UpdateSuggestedTagsTask.java b/app/src/main/java/io/lbry/browser/tasks/UpdateSuggestedTagsTask.java index c856027b..aee8dc1a 100644 --- a/app/src/main/java/io/lbry/browser/tasks/UpdateSuggestedTagsTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/UpdateSuggestedTagsTask.java @@ -14,18 +14,27 @@ import io.lbry.browser.utils.Lbry; public class UpdateSuggestedTagsTask extends AsyncTask> { private boolean clearPrevious; + private boolean excludeMature; private int limit; private String filter; private TagListAdapter addedTagsAdapter; private TagListAdapter suggestedTagsAdapter; private KnownTagsHandler handler; - public UpdateSuggestedTagsTask(String filter, int limit, TagListAdapter addedTagsAdapter, TagListAdapter suggestedTagsAdapter, boolean clearPrevious, KnownTagsHandler handler) { + public UpdateSuggestedTagsTask( + String filter, + int limit, + TagListAdapter addedTagsAdapter, + TagListAdapter suggestedTagsAdapter, + boolean clearPrevious, + boolean excludeMature, + KnownTagsHandler handler) { this.filter = filter; this.limit = limit; this.addedTagsAdapter = addedTagsAdapter; this.suggestedTagsAdapter = suggestedTagsAdapter; this.clearPrevious = clearPrevious; + this.excludeMature = excludeMature; this.handler = handler; } @@ -38,6 +47,9 @@ public class UpdateSuggestedTagsTask extends AsyncTask> { } while (tags.size() < limit) { Tag randomTag = Lbry.knownTags.get(random.nextInt(Lbry.knownTags.size())); + if (excludeMature && randomTag.isMature()) { + continue; + } if (!Lbry.followedTags.contains(randomTag) && (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(randomTag))) { tags.add(randomTag); } @@ -49,6 +61,9 @@ public class UpdateSuggestedTagsTask extends AsyncTask> { } for (int i = 0; i < Lbry.knownTags.size() && tags.size() < limit - 1; i++) { Tag knownTag = Lbry.knownTags.get(i); + if (excludeMature && knownTag.isMature()) { + continue; + } if ((knownTag.getLowercaseName().startsWith(filter) || knownTag.getLowercaseName().matches(filter)) && (!tags.contains(knownTag) && !Lbry.followedTags.contains(knownTag) && (addedTagsAdapter == null || !addedTagsAdapter.getTags().contains(knownTag)))) { diff --git a/app/src/main/java/io/lbry/browser/tasks/claim/AbandonChannelTask.java b/app/src/main/java/io/lbry/browser/tasks/claim/AbandonChannelTask.java index 38d3803f..3b71a45b 100644 --- a/app/src/main/java/io/lbry/browser/tasks/claim/AbandonChannelTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/claim/AbandonChannelTask.java @@ -42,7 +42,7 @@ public class AbandonChannelTask extends AsyncTask { try { Map options = new HashMap<>(); options.put("claim_id", claimId); - options.put("blocking", false); + options.put("blocking", true); JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_CHANNEL_ABANDON, options); successfulClaimIds.add(claimId); } catch (ApiCallException ex) { diff --git a/app/src/main/java/io/lbry/browser/tasks/claim/AbandonStreamTask.java b/app/src/main/java/io/lbry/browser/tasks/claim/AbandonStreamTask.java index 650ee98d..a5c68d93 100644 --- a/app/src/main/java/io/lbry/browser/tasks/claim/AbandonStreamTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/claim/AbandonStreamTask.java @@ -42,12 +42,10 @@ public class AbandonStreamTask extends AsyncTask { try { Map options = new HashMap<>(); options.put("claim_id", claimId); - options.put("blocking", false); + options.put("blocking", true); JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_STREAM_ABANDON, options); - android.util.Log.d("#HELP", result.toString()); successfulClaimIds.add(claimId); } catch (ApiCallException ex) { - android.util.Log.e("#HELP", ex.getMessage(), ex); failedClaimIds.add(claimId); failedExceptions.add(ex); } diff --git a/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchResultHandler.java b/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchResultHandler.java new file mode 100644 index 00000000..2f4bdd8c --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchResultHandler.java @@ -0,0 +1,10 @@ +package io.lbry.browser.tasks.claim; + +import java.util.List; + +import io.lbry.browser.model.Claim; + +public interface ClaimSearchResultHandler { + void onSuccess(List claims, boolean hasReachedEnd); + void onError(Exception error); +} diff --git a/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchTask.java b/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchTask.java index 7424d2eb..696eb60c 100644 --- a/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/claim/ClaimSearchTask.java @@ -2,7 +2,6 @@ package io.lbry.browser.tasks.claim; import android.os.AsyncTask; import android.view.View; -import android.widget.ProgressBar; import java.util.List; import java.util.Map; @@ -47,8 +46,4 @@ public class ClaimSearchTask extends AsyncTask> { } } - public interface ClaimSearchResultHandler { - void onSuccess(List claims, boolean hasReachedEnd); - void onError(Exception error); - } } diff --git a/app/src/main/java/io/lbry/browser/tasks/claim/PurchaseListTask.java b/app/src/main/java/io/lbry/browser/tasks/claim/PurchaseListTask.java new file mode 100644 index 00000000..46037086 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/claim/PurchaseListTask.java @@ -0,0 +1,70 @@ +package io.lbry.browser.tasks.claim; + +import android.os.AsyncTask; +import android.view.View; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.lbry.browser.exceptions.ApiCallException; +import io.lbry.browser.model.Claim; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbry; + +public class PurchaseListTask extends AsyncTask> { + private int page; + private int pageSize; + private ClaimSearchResultHandler handler; + private View progressView; + private Exception error; + + public PurchaseListTask(int page, int pageSize, View progressView, ClaimSearchResultHandler handler) { + this.page = page; + this.pageSize = pageSize; + this.progressView = progressView; + this.handler = handler; + } + + protected void onPreExecute() { + Helper.setViewVisibility(progressView, View.VISIBLE); + } + protected List doInBackground(Void... params) { + List claims = null; + try { + Map options = new HashMap<>(); + options.put("page", page); + options.put("page_size", pageSize); + options.put("resolve", true); + + JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PURCHASE_LIST, options); + JSONArray items = result.getJSONArray("items"); + claims = new ArrayList<>(); + for (int i = 0; i < items.length(); i++) { + Claim claim = Claim.fromJSONObject(items.getJSONObject(i).getJSONObject("claim")); + claims.add(claim); + + Lbry.addClaimToCache(claim); + } + } catch (ApiCallException | JSONException | ClassCastException ex) { + error = ex; + } + + return claims; + } + protected void onPostExecute(List claims) { + Helper.setViewVisibility(progressView, View.GONE); + if (handler != null) { + if (claims != null) { + handler.onSuccess(claims, claims.size() < pageSize); + } else { + handler.onError(error); + } + } + } +} diff --git a/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java b/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java index 154a5dc7..e6a35713 100644 --- a/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/localdata/LoadGalleryItemsTask.java @@ -62,9 +62,8 @@ public class LoadGalleryItemsTask extends AsyncTask claimSearchOptions = buildContentOptions(); - contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() { + contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { if (contentListAdapter == null) { diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java index 6c88ccfe..3e3e804a 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java @@ -1,7 +1,6 @@ package io.lbry.browser.ui.channel; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; @@ -32,6 +31,7 @@ import io.lbry.browser.dialog.ContentSortDialogFragment; import io.lbry.browser.listener.DownloadActionListener; import io.lbry.browser.model.Claim; import io.lbry.browser.model.LbryFile; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.claim.ClaimSearchTask; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; @@ -254,7 +254,7 @@ public class ChannelContentFragment extends Fragment implements DownloadActionLi contentClaimSearchLoading = true; Helper.setViewVisibility(noContentView, View.GONE); Map claimSearchOptions = buildContentOptions(); - contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() { + contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { if (contentListAdapter == null) { diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java index 721e8991..f87906c1 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java @@ -18,6 +18,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.core.content.ContextCompat; +import androidx.core.widget.NestedScrollView; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; @@ -53,6 +54,7 @@ import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; +import io.lbry.browser.utils.Predefined; import lombok.Getter; public class ChannelFormFragment extends BaseFragment implements @@ -70,6 +72,7 @@ public class ChannelFormFragment extends BaseFragment implements private TextView linkShowOptional; private MaterialButton buttonSave; + private NestedScrollView scrollView; private View inlineBalanceContainer; private TextView inlineBalanceValue; private View uploadProgress; @@ -111,6 +114,7 @@ public class ChannelFormFragment extends BaseFragment implements ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_channel_form, container, false); + scrollView = root.findViewById(R.id.channel_form_scroll_view); linkCancel = root.findViewById(R.id.channel_form_cancel_link); linkShowOptional = root.findViewById(R.id.channel_form_toggle_optional); buttonSave = root.findViewById(R.id.channel_form_save_button); @@ -136,6 +140,7 @@ public class ChannelFormFragment extends BaseFragment implements Context context = getContext(); FlexboxLayoutManager flm1 = new FlexboxLayoutManager(context); FlexboxLayoutManager flm2 = new FlexboxLayoutManager(context); + FlexboxLayoutManager flm3 = new FlexboxLayoutManager(context); addedTagsList = root.findViewById(R.id.form_added_tags); addedTagsList.setLayoutManager(flm1); suggestedTagsList = root.findViewById(R.id.form_suggested_tags); @@ -145,6 +150,7 @@ public class ChannelFormFragment extends BaseFragment implements addedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_REMOVE); addedTagsAdapter.setClickListener(this); addedTagsList.setAdapter(addedTagsAdapter); + suggestedTagsAdapter = new TagListAdapter(new ArrayList<>(), getContext()); suggestedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD); suggestedTagsAdapter.setClickListener(this); @@ -167,6 +173,12 @@ public class ChannelFormFragment extends BaseFragment implements if (containerOptionalFields.getVisibility() != View.VISIBLE) { containerOptionalFields.setVisibility(View.VISIBLE); linkShowOptional.setText(R.string.hide_optional_fields); + scrollView.post(new Runnable() { + @Override + public void run() { + scrollView.fullScroll(NestedScrollView.FOCUS_DOWN); + } + }); } else { containerOptionalFields.setVisibility(View.GONE); linkShowOptional.setText(R.string.show_optional_fields); @@ -549,8 +561,11 @@ public class ChannelFormFragment extends BaseFragment implements Snackbar.make(getView(), getString(R.string.tag_already_added, tag.getName()), Snackbar.LENGTH_LONG).show(); return; } + if (addedTagsAdapter.getItemCount() == 5) { + Snackbar.make(getView(), R.string.tag_limit_reached, Snackbar.LENGTH_LONG).show(); + return; + } - tag.setFollowed(true); addedTagsAdapter.addTag(tag); if (suggestedTagsAdapter != null) { suggestedTagsAdapter.removeTag(tag); @@ -561,7 +576,6 @@ public class ChannelFormFragment extends BaseFragment implements checkNoTagResults(); } public void removeTag(Tag tag) { - tag.setFollowed(false); addedTagsAdapter.removeTag(tag); updateSuggestedTags(currentFilter, SUGGESTED_LIMIT, false); checkNoAddedTags(); @@ -581,7 +595,14 @@ public class ChannelFormFragment extends BaseFragment implements } private void updateSuggestedTags(String filter, int limit, boolean clearPrevious) { - UpdateSuggestedTagsTask task = new UpdateSuggestedTagsTask(filter, limit, addedTagsAdapter, suggestedTagsAdapter, clearPrevious, new UpdateSuggestedTagsTask.KnownTagsHandler() { + UpdateSuggestedTagsTask task = new UpdateSuggestedTagsTask( + filter, + limit, + addedTagsAdapter, + suggestedTagsAdapter, + clearPrevious, + false, + new UpdateSuggestedTagsTask.KnownTagsHandler() { @Override public void onSuccess(List tags) { if (suggestedTagsAdapter == null) { diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java index eef59baa..4f2454e2 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java @@ -257,7 +257,6 @@ public class ChannelManagerFragment extends BaseFragment implements ActionMode.C if (R.id.action_delete == menuItem.getItemId()) { if (adapter != null && adapter.getSelectedCount() > 0) { final List selectedClaims = new ArrayList<>(adapter.getSelectedItems()); - android.util.Log.d("#HELP", selectedClaims.toString()); String message = getResources().getQuantityString(R.plurals.confirm_delete_channels, selectedClaims.size()); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()). setTitle(R.string.delete_selection). diff --git a/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java b/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java index 221db707..aab5d4cc 100644 --- a/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java @@ -1,7 +1,6 @@ package io.lbry.browser.ui.editorschoice; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; @@ -27,6 +26,7 @@ import io.lbry.browser.R; import io.lbry.browser.adapter.EditorsChoiceItemAdapter; import io.lbry.browser.model.Claim; import io.lbry.browser.model.EditorsChoiceItem; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.claim.ClaimSearchTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; @@ -105,7 +105,7 @@ public class EditorsChoiceFragment extends BaseFragment { } contentLoading = true; - ClaimSearchTask task = new ClaimSearchTask(buildContentOptions(), Lbry.LBRY_TV_CONNECTION_STRING, loading, new ClaimSearchTask.ClaimSearchResultHandler() { + ClaimSearchTask task = new ClaimSearchTask(buildContentOptions(), Lbry.LBRY_TV_CONNECTION_STRING, loading, new ClaimSearchResultHandler() { @Override public void onSuccess(List items, boolean hasReachedEnd) { List data = buildDataFromClaims(items); diff --git a/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java b/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java index 72846319..9f2bfe50 100644 --- a/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/following/FileViewFragment.java @@ -30,7 +30,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.ContextCompat; -import androidx.core.widget.NestedScrollView; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -83,7 +82,6 @@ import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.listener.DownloadActionListener; import io.lbry.browser.listener.SdkStatusListener; import io.lbry.browser.listener.StoragePermissionListener; -import io.lbry.browser.listener.WalletBalanceListener; import io.lbry.browser.model.Claim; import io.lbry.browser.model.ClaimCacheKey; import io.lbry.browser.model.Fee; @@ -97,7 +95,7 @@ import io.lbry.browser.tasks.LighthouseSearchTask; import io.lbry.browser.tasks.ReadTextFileTask; import io.lbry.browser.tasks.SetSdkSettingTask; import io.lbry.browser.tasks.claim.ClaimListResultHandler; -import io.lbry.browser.tasks.claim.ClaimSearchTask; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.claim.ResolveTask; import io.lbry.browser.tasks.file.DeleteFileTask; import io.lbry.browser.tasks.file.FileListTask; @@ -944,6 +942,15 @@ public class FileViewFragment extends BaseFragment implements ImageView descIndicator = root.findViewById(R.id.file_view_desc_toggle_arrow); descIndicator.setImageResource(R.drawable.ic_arrow_dropdown); + boolean hasDescription = !Helper.isNullOrEmpty(claim.getDescription()); + boolean hasTags = claim.getTags() != null && claim.getTags().size() > 0; + + root.findViewById(R.id.file_view_description).setVisibility(hasDescription ? View.VISIBLE : View.GONE); + root.findViewById(R.id.file_view_tag_area).setVisibility(hasTags ? View.VISIBLE : View.GONE); + if (hasTags && !hasDescription) { + root.findViewById(R.id.file_view_tag_area).setPadding(0, 0, 0, 0); + } + root.findViewById(R.id.file_view_description_area).setVisibility(View.GONE); ((TextView) root.findViewById(R.id.file_view_title)).setText(claim.getTitle()); ((TextView) root.findViewById(R.id.file_view_description)).setText(claim.getDescription()); @@ -1076,39 +1083,42 @@ public class FileViewFragment extends BaseFragment implements newPlayerCreated = true; } - PlayerView view = getView().findViewById(R.id.file_view_exoplayer_view); - view.setShutterBackgroundColor(Color.TRANSPARENT); - view.setPlayer(MainActivity.appPlayer); - view.setUseController(true); - if (context instanceof MainActivity) { - ((MainActivity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - - if (MainActivity.nowPlayingClaim != null && - MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId()) && - !newPlayerCreated) { - // if the claim is already playing, we don't need to reload the media source - return; - } - - if (MainActivity.appPlayer != null) { - showBuffering(); - if (fileViewPlayerListener != null) { - MainActivity.appPlayer.addListener(fileViewPlayerListener); - } + View root = getView(); + if (root != null) { + PlayerView view = root.findViewById(R.id.file_view_exoplayer_view); + view.setShutterBackgroundColor(Color.TRANSPARENT); + view.setPlayer(MainActivity.appPlayer); + view.setUseController(true); if (context instanceof MainActivity) { - ((MainActivity) context).setNowPlayingClaim(claim); + ((MainActivity) context).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - MainActivity.appPlayer.setPlayWhenReady(true); - String userAgent = Util.getUserAgent(context, getString(R.string.app_name)); - String mediaSourceUrl = getStreamingUrl(); - MediaSource mediaSource = new ProgressiveMediaSource.Factory( - new CacheDataSourceFactory(MainActivity.playerCache, new DefaultDataSourceFactory(context, userAgent)), - new DefaultExtractorsFactory() - ).createMediaSource(Uri.parse(mediaSourceUrl)); + if (MainActivity.nowPlayingClaim != null && + MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId()) && + !newPlayerCreated) { + // if the claim is already playing, we don't need to reload the media source + return; + } - MainActivity.appPlayer.prepare(mediaSource, true, true); + if (MainActivity.appPlayer != null) { + showBuffering(); + if (fileViewPlayerListener != null) { + MainActivity.appPlayer.addListener(fileViewPlayerListener); + } + if (context instanceof MainActivity) { + ((MainActivity) context).setNowPlayingClaim(claim, currentUrl); + } + + MainActivity.appPlayer.setPlayWhenReady(true); + String userAgent = Util.getUserAgent(context, getString(R.string.app_name)); + String mediaSourceUrl = getStreamingUrl(); + MediaSource mediaSource = new ProgressiveMediaSource.Factory( + new CacheDataSourceFactory(MainActivity.playerCache, new DefaultDataSourceFactory(context, userAgent)), + new DefaultExtractorsFactory() + ).createMediaSource(Uri.parse(mediaSourceUrl)); + + MainActivity.appPlayer.prepare(mediaSource, true, true); + } } } @@ -1473,7 +1483,7 @@ public class FileViewFragment extends BaseFragment implements SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); LighthouseSearchTask relatedTask = new LighthouseSearchTask( - title, RELATED_CONTENT_SIZE, 0, canShowMatureContent, claimId, relatedLoading, new ClaimSearchTask.ClaimSearchResultHandler() { + title, RELATED_CONTENT_SIZE, 0, canShowMatureContent, claimId, relatedLoading, new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { List filteredClaims = new ArrayList<>(); @@ -1746,7 +1756,13 @@ public class FileViewFragment extends BaseFragment implements amountClaimed == 1 ? 1 : 2, new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)); } - Snackbar.make(getView().findViewById(R.id.file_view_global_layout), message, Snackbar.LENGTH_LONG).show(); + View root = getView(); + Context context = getContext(); + if (root != null) { + Snackbar.make(root.findViewById(R.id.file_view_global_layout), message, Snackbar.LENGTH_LONG).show(); + } else if (context instanceof MainActivity) { + ((MainActivity) context).showMessage(message); + } } @Override @@ -1759,12 +1775,16 @@ public class FileViewFragment extends BaseFragment implements if (claim == null) { return; } - if (claim.getFile() != null && claim.getFile().isCompleted()) { - Helper.setViewVisibility(getView().findViewById(R.id.file_view_action_delete), View.VISIBLE); - Helper.setViewVisibility(getView().findViewById(R.id.file_view_action_download), View.GONE); - } else { - Helper.setViewVisibility(getView().findViewById(R.id.file_view_action_delete), View.GONE); - Helper.setViewVisibility(getView().findViewById(R.id.file_view_action_download), View.VISIBLE); + View root = getView(); + if (root != null) { + if (claim.getFile() != null && claim.getFile().isCompleted()) { + Helper.setViewVisibility(root.findViewById(R.id.file_view_action_delete), View.VISIBLE); + Helper.setViewVisibility(root.findViewById(R.id.file_view_action_download), View.GONE); + } else { + Helper.setViewVisibility(root.findViewById(R.id.file_view_action_delete), View.GONE); + Helper.setViewVisibility(root.findViewById(R.id.file_view_action_download), View.VISIBLE); + } + } } diff --git a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java index 35748650..b64c6fe9 100644 --- a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java @@ -1,7 +1,6 @@ package io.lbry.browser.ui.following; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; @@ -37,11 +36,11 @@ import io.lbry.browser.dialog.ContentFromDialogFragment; import io.lbry.browser.dialog.ContentSortDialogFragment; import io.lbry.browser.dialog.DiscoverDialogFragment; import io.lbry.browser.exceptions.LbryUriException; -import io.lbry.browser.listener.DarkThemeChangeListener; import io.lbry.browser.listener.DownloadActionListener; import io.lbry.browser.model.Claim; import io.lbry.browser.model.LbryFile; import io.lbry.browser.model.lbryinc.Subscription; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.lbryinc.ChannelSubscribeTask; import io.lbry.browser.tasks.claim.ClaimListResultHandler; import io.lbry.browser.tasks.claim.ClaimSearchTask; @@ -59,7 +58,6 @@ import io.lbry.browser.utils.Predefined; public class FollowingFragment extends BaseFragment implements FetchSubscriptionsTask.FetchSubscriptionsHandler, ChannelItemSelectionListener, - DarkThemeChangeListener, DownloadActionListener, SharedPreferences.OnSharedPreferenceChangeListener { @@ -585,7 +583,7 @@ public class FollowingFragment extends BaseFragment implements contentClaimSearchLoading = true; Helper.setViewVisibility(noContentView, View.GONE); Map claimSearchOptions = buildContentOptions(); - contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() { + contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { if (contentListAdapter == null) { @@ -651,7 +649,7 @@ public class FollowingFragment extends BaseFragment implements buildSuggestedOptions(), Lbry.LBRY_TV_CONNECTION_STRING, suggestedChannelAdapter == null || suggestedChannelAdapter.getItemCount() == 0 ? bigContentLoading : contentLoading, - new ClaimSearchTask.ClaimSearchResultHandler() { + new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { suggestedHasReachedEnd = hasReachedEnd; @@ -804,13 +802,4 @@ public class FollowingFragment extends BaseFragment implements // invalid file info for download } } - - public void onDarkThemeToggled() { - Helper.refreshRecyclerView(contentList); - Helper.refreshRecyclerView(horizontalChannelList); - Helper.refreshRecyclerView(suggestedChannelGrid); - if (discoverDialog != null) { - //discoverDialog.onDarkThemeToggled(); - } - } } diff --git a/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java b/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java index e661d737..22a11a9e 100644 --- a/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/library/LibraryFragment.java @@ -47,6 +47,8 @@ import io.lbry.browser.model.ViewHistory; import io.lbry.browser.tasks.claim.AbandonChannelTask; import io.lbry.browser.tasks.claim.AbandonHandler; import io.lbry.browser.tasks.claim.ClaimListResultHandler; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; +import io.lbry.browser.tasks.claim.PurchaseListTask; import io.lbry.browser.tasks.claim.ResolveTask; import io.lbry.browser.tasks.file.BulkDeleteFilesTask; import io.lbry.browser.tasks.file.DeleteFileTask; @@ -63,7 +65,8 @@ public class LibraryFragment extends BaseFragment implements ActionMode.Callback, DownloadActionListener, SelectionModeListener, SdkStatusListener { private static final int FILTER_DOWNLOADS = 1; - private static final int FILTER_HISTORY = 2; + private static final int FILTER_PURCHASES = 2; + private static final int FILTER_HISTORY = 3; private static final int PAGE_SIZE = 50; private ActionMode actionMode; @@ -74,6 +77,7 @@ public class LibraryFragment extends BaseFragment implements private ClaimListAdapter contentListAdapter; private ProgressBar listLoading; private TextView linkFilterDownloads; + private TextView linkFilterPurchases; private TextView linkFilterHistory; private View layoutListEmpty; private TextView textListEmpty; @@ -119,6 +123,7 @@ public class LibraryFragment extends BaseFragment implements listLoading = root.findViewById(R.id.library_list_loading); linkFilterDownloads = root.findViewById(R.id.library_filter_link_downloads); + linkFilterPurchases = root.findViewById(R.id.library_filter_link_purchases); linkFilterHistory = root.findViewById(R.id.library_filter_link_history); layoutListEmpty = root.findViewById(R.id.library_empty_container); @@ -131,6 +136,12 @@ public class LibraryFragment extends BaseFragment implements showDownloads(); } }); + linkFilterPurchases.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + showPurchases(); + } + }); linkFilterHistory.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -247,8 +258,10 @@ public class LibraryFragment extends BaseFragment implements private void showDownloads() { currentFilter = FILTER_DOWNLOADS; linkFilterDownloads.setTypeface(null, Typeface.BOLD); + linkFilterPurchases.setTypeface(null, Typeface.NORMAL); linkFilterHistory.setTypeface(null, Typeface.NORMAL); if (contentListAdapter != null) { + contentListAdapter.setHideFee(false); contentListAdapter.clearItems(); contentListAdapter.setCanEnterSelectionMode(true); } @@ -262,14 +275,38 @@ public class LibraryFragment extends BaseFragment implements } } + private void showPurchases() { + currentFilter = FILTER_PURCHASES; + linkFilterDownloads.setTypeface(null, Typeface.NORMAL); + linkFilterPurchases.setTypeface(null, Typeface.BOLD); + linkFilterHistory.setTypeface(null, Typeface.NORMAL); + if (contentListAdapter != null) { + contentListAdapter.setHideFee(true); + contentListAdapter.clearItems(); + contentListAdapter.setCanEnterSelectionMode(true); + } + listReachedEnd = false; + + cardStats.setVisibility(View.GONE); + checkStatsLink(); + + layoutSdkInitializing.setVisibility(Lbry.SDK_READY ? View.GONE : View.VISIBLE); + currentPage = 1; + if (Lbry.SDK_READY) { + fetchPurchases(); + } + } + private void showHistory() { currentFilter = FILTER_HISTORY; linkFilterDownloads.setTypeface(null, Typeface.NORMAL); + linkFilterPurchases.setTypeface(null, Typeface.NORMAL); linkFilterHistory.setTypeface(null, Typeface.BOLD); if (actionMode != null) { actionMode.finish(); } if (contentListAdapter != null) { + contentListAdapter.setHideFee(false); contentListAdapter.clearItems(); contentListAdapter.setCanEnterSelectionMode(false); } @@ -287,6 +324,7 @@ public class LibraryFragment extends BaseFragment implements contentListAdapter = new ClaimListAdapter(claims, getContext()); contentListAdapter.setCanEnterSelectionMode(true); contentListAdapter.setSelectionModeListener(this); + contentListAdapter.setHideFee(currentFilter != FILTER_PURCHASES); contentListAdapter.setListener(new ClaimListAdapter.ClaimListItemListener() { @Override public void onClaimClicked(Claim claim) { @@ -342,6 +380,36 @@ public class LibraryFragment extends BaseFragment implements task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + 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 claims, boolean hasReachedEnd) { + listReachedEnd = hasReachedEnd; + if (contentListAdapter == null) { + initContentListAdapter(claims); + } else { + contentListAdapter.addItems(claims); + } + if (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); + } + private void fetchHistory() { contentListLoading = true; Helper.setViewVisibility(layoutListEmpty, View.GONE); @@ -397,7 +465,13 @@ public class LibraryFragment extends BaseFragment implements private void checkListEmpty() { layoutListEmpty.setVisibility(contentListAdapter == null || contentListAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); - textListEmpty.setText(currentFilter == FILTER_DOWNLOADS ? R.string.library_no_downloads : R.string.library_no_history); + int stringResourceId; + switch (currentFilter) { + case FILTER_DOWNLOADS: default: stringResourceId = R.string.library_no_downloads; break; + case FILTER_HISTORY: stringResourceId = R.string.library_no_history; break; + case FILTER_PURCHASES: stringResourceId = R.string.library_no_purchases; break; + } + textListEmpty.setText(stringResourceId); } private void addFiles(List files) { @@ -487,7 +561,8 @@ public class LibraryFragment extends BaseFragment implements private void checkStatsLink() { linkStats.setVisibility(cardStats.getVisibility() == View.VISIBLE || listLoading.getVisibility() == View.VISIBLE || - currentFilter == FILTER_HISTORY ? + currentFilter != FILTER_DOWNLOADS || + !Lbry.SDK_READY ? View.GONE : View.VISIBLE); } diff --git a/app/src/main/java/io/lbry/browser/ui/other/SettingsFragment.java b/app/src/main/java/io/lbry/browser/ui/other/SettingsFragment.java index 9a209b30..7f5cb690 100644 --- a/app/src/main/java/io/lbry/browser/ui/other/SettingsFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/other/SettingsFragment.java @@ -69,11 +69,6 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_DARK_MODE)) { boolean darkMode = sp.getBoolean(MainActivity.PREFERENCE_KEY_DARK_MODE, false); AppCompatDelegate.setDefaultNightMode(darkMode ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO); - - Context context = getContext(); - if (context instanceof MainActivity) { - ((MainActivity) context).onThemeChanged(); - } } } } diff --git a/app/src/main/java/io/lbry/browser/ui/publish/PublishFormFragment.java b/app/src/main/java/io/lbry/browser/ui/publish/PublishFormFragment.java index a2335306..69d1e076 100644 --- a/app/src/main/java/io/lbry/browser/ui/publish/PublishFormFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/publish/PublishFormFragment.java @@ -1,26 +1,546 @@ package io.lbry.browser.ui.publish; +import android.content.Context; +import android.graphics.Color; +import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; +import android.text.Editable; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.webkit.MimeTypeMap; +import android.widget.AdapterView; +import android.widget.CompoundButton; +import android.widget.ProgressBar; +import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.AppCompatSpinner; +import androidx.core.widget.NestedScrollView; +import androidx.recyclerview.widget.RecyclerView; +import com.google.android.flexbox.FlexboxLayoutManager; +import com.google.android.material.button.MaterialButton; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.switchmaterial.SwitchMaterial; +import com.google.android.material.textfield.TextInputEditText; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import io.lbry.browser.BuildConfig; +import io.lbry.browser.MainActivity; import io.lbry.browser.R; +import io.lbry.browser.adapter.InlineChannelSpinnerAdapter; +import io.lbry.browser.adapter.TagListAdapter; +import io.lbry.browser.listener.SdkStatusListener; +import io.lbry.browser.listener.StoragePermissionListener; +import io.lbry.browser.listener.WalletBalanceListener; +import io.lbry.browser.model.Claim; +import io.lbry.browser.model.GalleryItem; +import io.lbry.browser.model.NavMenuItem; +import io.lbry.browser.model.Tag; +import io.lbry.browser.model.WalletBalance; +import io.lbry.browser.tasks.ChannelCreateUpdateTask; +import io.lbry.browser.tasks.UpdateSuggestedTagsTask; +import io.lbry.browser.tasks.claim.ClaimListResultHandler; +import io.lbry.browser.tasks.claim.ClaimListTask; +import io.lbry.browser.tasks.claim.ClaimResultHandler; +import io.lbry.browser.tasks.lbryinc.LogPublishTask; import io.lbry.browser.ui.BaseFragment; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; +import io.lbry.browser.utils.LbryUri; +import io.lbry.browser.utils.Predefined; -public class PublishFormFragment extends BaseFragment { +public class PublishFormFragment extends BaseFragment implements + SdkStatusListener, StoragePermissionListener, TagListAdapter.TagClickListener, WalletBalanceListener { + + private static final int SUGGESTED_LIMIT = 8; + + private boolean editMode; + private boolean fetchingChannels; + private String currentFilter; + + private TextInputEditText inputTagFilter; + private RecyclerView addedTagsList; + private RecyclerView suggestedTagsList; + private RecyclerView matureTagsList; + private TagListAdapter addedTagsAdapter; + private TagListAdapter suggestedTagsAdapter; + private TagListAdapter matureTagsAdapter; + private ProgressBar progressLoadingChannels; + private View noTagsView; + private View noTagResultsView; + + private InlineChannelSpinnerAdapter channelSpinnerAdapter; + private AppCompatSpinner channelSpinner; + private AppCompatSpinner priceCurrencySpinner; + private AppCompatSpinner languageSpinner; + private AppCompatSpinner licenseSpinner; + + private NestedScrollView scrollView; + private View layoutExtraFields; + private TextView linkShowExtraFields; + private View textNoPrice; + private View layoutPrice; + private SwitchMaterial switchPrice; + + private TextInputEditText inputTitle; + private TextInputEditText inputDescription; + private TextInputEditText inputPrice; + private TextInputEditText inputAddress; + private TextInputEditText inputDeposit; + private View inlineDepositBalanceContainer; + private TextView inlineDepositBalanceValue; + + private View linkCancel; + private MaterialButton buttonPublish; + + private View inlineChannelCreator; + private TextInputEditText inlineChannelCreatorInputName; + private TextInputEditText inlineChannelCreatorInputDeposit; + private View inlineChannelCreatorInlineBalance; + private TextView inlineChannelCreatorInlineBalanceValue; + private View inlineChannelCreatorCancelLink; + private View inlineChannelCreatorProgress; + private MaterialButton inlineChannelCreatorCreateButton; + + private String uploadedThumbnailUrl; + private boolean editFieldsLoaded; + private Claim currentClaim; + private GalleryItem currentGalleryItem; + private String currentFilePath; + private boolean fileLoaded; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.fragment_publishes, container, false); + View root = inflater.inflate(R.layout.fragment_publish_form, container, false); + scrollView = root.findViewById(R.id.publish_form_scroll_view); + progressLoadingChannels = root.findViewById(R.id.publish_form_loading_channels); + channelSpinner = root.findViewById(R.id.publish_form_channel_spinner); + inputTagFilter = root.findViewById(R.id.form_tag_filter_input); + noTagsView = root.findViewById(R.id.form_no_added_tags); + noTagResultsView = root.findViewById(R.id.form_no_tag_results); + + layoutExtraFields = root.findViewById(R.id.publish_form_extra_options_container); + linkShowExtraFields = root.findViewById(R.id.publish_form_toggle_extra); + layoutPrice = root.findViewById(R.id.publish_form_price_container); + textNoPrice = root.findViewById(R.id.publish_form_no_price); + switchPrice = root.findViewById(R.id.publish_form_price_switch); + + inputTitle = root.findViewById(R.id.publish_form_input_title); + inputDeposit = root.findViewById(R.id.publish_form_input_description); + inputPrice = root.findViewById(R.id.publish_form_input_price); + inputAddress = root.findViewById(R.id.publish_form_input_address); + inputDeposit = root.findViewById(R.id.publish_form_input_deposit); + + linkCancel = root.findViewById(R.id.publish_form_cancel); + buttonPublish = root.findViewById(R.id.publish_form_publish_button); + + Context context = getContext(); + FlexboxLayoutManager flm1 = new FlexboxLayoutManager(context); + FlexboxLayoutManager flm2 = new FlexboxLayoutManager(context); + FlexboxLayoutManager flm3 = new FlexboxLayoutManager(context); + addedTagsList = root.findViewById(R.id.form_added_tags); + addedTagsList.setLayoutManager(flm1); + suggestedTagsList = root.findViewById(R.id.form_suggested_tags); + suggestedTagsList.setLayoutManager(flm2); + + root.findViewById(R.id.form_mature_tags_container).setVisibility(View.VISIBLE); + matureTagsList = root.findViewById(R.id.form_mature_tags); + matureTagsList.setLayoutManager(flm3); + + addedTagsAdapter = new TagListAdapter(new ArrayList<>(), context); + addedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_REMOVE); + addedTagsAdapter.setClickListener(this); + addedTagsList.setAdapter(addedTagsAdapter); + + suggestedTagsAdapter = new TagListAdapter(new ArrayList<>(), getContext()); + suggestedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD); + suggestedTagsAdapter.setClickListener(this); + suggestedTagsList.setAdapter(suggestedTagsAdapter); + + matureTagsAdapter = new TagListAdapter(Helper.getTagObjectsForTags(Predefined.MATURE_TAGS), context); + matureTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD); + matureTagsAdapter.setClickListener(this); + matureTagsList.setAdapter(matureTagsAdapter); + + inlineChannelCreator = root.findViewById(R.id.container_inline_channel_form_create); + inlineChannelCreatorInputName = root.findViewById(R.id.inline_channel_form_input_name); + inlineChannelCreatorInputDeposit = root.findViewById(R.id.inline_channel_form_input_deposit); + inlineChannelCreatorInlineBalance = root.findViewById(R.id.inline_channel_form_inline_balance_container); + inlineChannelCreatorInlineBalanceValue = root.findViewById(R.id.inline_channel_form_inline_balance_value); + inlineChannelCreatorProgress = root.findViewById(R.id.inline_channel_form_create_progress); + inlineChannelCreatorCancelLink = root.findViewById(R.id.inline_channel_form_cancel_link); + inlineChannelCreatorCreateButton = root.findViewById(R.id.inline_channel_form_create_button); + + initUi(); return root; } + private void initUi() { + + inputAddress.setText(Helper.generateUrl()); + + switchPrice.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + Helper.setViewVisibility(textNoPrice, checked ? View.GONE : View.VISIBLE); + Helper.setViewVisibility(layoutPrice, checked ? View.VISIBLE : View.GONE); + } + }); + + inputDeposit.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + Helper.setViewVisibility(inlineDepositBalanceContainer, hasFocus ? View.VISIBLE : View.GONE); + } + }); + + linkShowExtraFields.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (layoutExtraFields.getVisibility() != View.VISIBLE) { + layoutExtraFields.setVisibility(View.VISIBLE); + linkShowExtraFields.setText(R.string.hide_extra_fields); + scrollView.post(new Runnable() { + @Override + public void run() { + scrollView.fullScroll(NestedScrollView.FOCUS_DOWN); + } + }); + } else { + layoutExtraFields.setVisibility(View.GONE); + linkShowExtraFields.setText(R.string.show_extra_fields); + } + } + }); + + linkCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Context context = getContext(); + if (context instanceof MainActivity) { + ((MainActivity) context).onBackPressed(); + } + } + }); + + channelSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int position, long l) { + Object item = adapterView.getItemAtPosition(position); + if (item instanceof Claim) { + Claim claim = (Claim) item; + if (claim.isPlaceholder() && !claim.isPlaceholderAnonymous()) { + if (!fetchingChannels) { + showInlineChannelCreator(); + } + } else { + hideInlineChannelCreator(); + } + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + + inputTagFilter.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) { + String value = Helper.getValue(charSequence); + setFilter(value); + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + + buttonPublish.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + } + }); + + setupInlineChannelCreator( + inlineChannelCreator, + inlineChannelCreatorInputName, + inlineChannelCreatorInputDeposit, + inlineChannelCreatorInlineBalance, + inlineChannelCreatorInlineBalanceValue, + inlineChannelCreatorCancelLink, + inlineChannelCreatorCreateButton, + inlineChannelCreatorProgress + ); + } + + @Override + public void onStart() { + super.onStart(); + MainActivity activity = (MainActivity) getContext(); + if (activity != null) { + activity.hideSearchBar(); + activity.showNavigationBackIcon(); + activity.lockDrawer(); + activity.hideFloatingWalletBalance(); + activity.addWalletBalanceListener(this); + + ActionBar actionBar = activity.getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(editMode ? R.string.edit_content : R.string.new_publish); + } + } + } + + @Override + public void onStop() { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) getContext(); + activity.removeWalletBalanceListener(this); + activity.restoreToggle(); + activity.showFloatingWalletBalance(); + if (!MainActivity.startingFilePickerActivity) { + activity.removeNavFragment(PublishFormFragment.class, NavMenuItem.ID_ITEM_NEW_PUBLISH); + } + } + super.onStop(); + } + + private void checkParams() { + Map params = getParams(); + if (params != null) { + if (params.containsKey("claim")) { + Claim claim = (Claim) params.get("claim"); + if (claim != null && !claim.equals(this.currentClaim)) { + this.currentClaim = claim; + editFieldsLoaded = false; + } + } + + if (params.containsKey("galleryItem")) { + currentGalleryItem = (GalleryItem) params.get("galleryItem"); + } else if (params.containsKey("directFilePath")) { + currentFilePath = (String) params.get("directFilePath"); + } + } else { + // shouldn't actually happen + cancelOnFatalCondition(getString(R.string.no_file_found)); + } + } + + private void updateFieldsFromCurrentClaim() { + if (currentClaim != null && !editFieldsLoaded) { + + + } + } + + private void checkPublishFile() { + String filePath = ""; + if (currentGalleryItem != null) { + // check gallery item type + filePath = currentGalleryItem.getFilePath(); + } else if (currentFilePath != null) { + filePath = currentFilePath; + } + + android.util.Log.d("#HELP", "filePath=" + filePath); + File file = new File(filePath); + if (!file.exists()) { + // file doesn't exist. although this shouldn't happen + cancelOnFatalCondition(getString(R.string.no_file_found)); + return; + } + + + + // check content type + String type = null; + String extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); + if (extension != null) { + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + android.util.Log.d("#HELP", "fileType=" + type); + + if (!Helper.isNullOrEmpty(type) && type.startsWith("video")) { + // ffmpeg video handling + } + } + + private void cancelOnFatalCondition(String message) { + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.showError(message); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + activity.onBackPressed(); + } + }, 100); + } + } + + public void onResume() { + super.onResume(); + if (!Lbry.SDK_READY) { + cancelOnFatalCondition(getString(R.string.sdk_initializing_functionality)); + return; + } + + checkParams(); + updateFieldsFromCurrentClaim(); + + if (currentClaim == null && (currentGalleryItem != null || (Helper.isNullOrEmpty(currentFilePath)))) { + // load file information + checkPublishFile(); + } + + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Channel Form", "ChannelForm"); + activity.addStoragePermissionListener(this); + if (editMode) { + ActionBar actionBar = activity.getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(R.string.edit_content); + } + } + } + + if (!Lbry.SDK_READY) { + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + activity.addSdkStatusListener(this); + } + } else { + onSdkReady(); + } + + String filterText = Helper.getValue(inputTagFilter.getText()); + updateSuggestedTags(filterText, SUGGESTED_LIMIT, true); + } + + public void onSdkReady() { + fetchChannels(); + onWalletBalanceUpdated(Lbry.walletBalance); + } + + private void fetchChannels() { + if (Lbry.ownChannels != null && Lbry.ownChannels.size() > 0) { + updateChannelList(Lbry.ownChannels); + return; + } + + fetchingChannels = true; + disableChannelSpinner(); + ClaimListTask task = new ClaimListTask(Claim.TYPE_CHANNEL, progressLoadingChannels, new ClaimListResultHandler() { + @Override + public void onSuccess(List claims) { + Lbry.ownChannels = new ArrayList<>(claims); + updateChannelList(Lbry.ownChannels); + enableChannelSpinner(); + fetchingChannels = false; + } + + @Override + public void onError(Exception error) { + enableChannelSpinner(); + fetchingChannels = false; + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + private void disableChannelSpinner() { + Helper.setViewEnabled(channelSpinner, false); + hideInlineChannelCreator(); + } + private void enableChannelSpinner() { + Helper.setViewEnabled(channelSpinner, true); + if (channelSpinner != null) { + Claim selectedClaim = (Claim) channelSpinner.getSelectedItem(); + if (selectedClaim != null) { + if (selectedClaim.isPlaceholder()) { + showInlineChannelCreator(); + } else { + hideInlineChannelCreator(); + } + } + } + } + private void showInlineChannelCreator() { + Helper.setViewVisibility(inlineChannelCreator, View.VISIBLE); + } + private void hideInlineChannelCreator() { + Helper.setViewVisibility(inlineChannelCreator, View.GONE); + } + + private void updateChannelList(List channels) { + if (channelSpinnerAdapter == null) { + Context context = getContext(); + if (context != null) { + channelSpinnerAdapter = new InlineChannelSpinnerAdapter(context, R.layout.spinner_item_channel, new ArrayList<>(channels)); + channelSpinnerAdapter.addPlaceholder(true); + channelSpinnerAdapter.notifyDataSetChanged(); + } + } else { + channelSpinnerAdapter.clear(); + channelSpinnerAdapter.addAll(channels); + channelSpinnerAdapter.addPlaceholder(true); + channelSpinnerAdapter.notifyDataSetChanged(); + } + + if (channelSpinner != null) { + channelSpinner.setAdapter(channelSpinnerAdapter); + } + + if (channelSpinnerAdapter != null && channelSpinner != null) { + if (channelSpinnerAdapter.getCount() > 2) { + // if anonymous displayed, select first channel if available + channelSpinner.setSelection(2); + } else if (channelSpinnerAdapter.getCount() > 1) { + // select anonymous + channelSpinner.setSelection(1); + } + } + } + + private Claim buildPublishClaim() { + Claim claim = new Claim(); + + return claim; + } + + private boolean validatePublishClaim() { + return false; + } + + @Override public boolean shouldHideGlobalPlayer() { return true; @@ -30,4 +550,214 @@ public class PublishFormFragment extends BaseFragment { public boolean shouldSuspendGlobalPlayer() { return true; } + + @Override + public void onTagClicked(Tag tag, int customizeMode) { + if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_ADD) { + addTag(tag); + } else if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_REMOVE) { + removeTag(tag); + } + } + + public void setFilter(String filter) { + currentFilter = filter; + updateSuggestedTags(currentFilter, SUGGESTED_LIMIT, true); + } + private void checkNoAddedTags() { + Helper.setViewVisibility(noTagsView, addedTagsAdapter == null || addedTagsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + private void checkNoTagResults() { + Helper.setViewVisibility(noTagResultsView, suggestedTagsAdapter == null || suggestedTagsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + public void addTag(Tag tag) { + if (addedTagsAdapter.getTags().contains(tag)) { + Snackbar.make(getView(), getString(R.string.tag_already_added, tag.getName()), Snackbar.LENGTH_LONG).show(); + return; + } + if (addedTagsAdapter.getItemCount() == 5) { + Snackbar.make(getView(), R.string.tag_limit_reached, Snackbar.LENGTH_LONG).show(); + return; + } + + addedTagsAdapter.addTag(tag); + if (suggestedTagsAdapter != null) { + suggestedTagsAdapter.removeTag(tag); + } + updateSuggestedTags(currentFilter, SUGGESTED_LIMIT, false); + + checkNoAddedTags(); + checkNoTagResults(); + } + public void removeTag(Tag tag) { + addedTagsAdapter.removeTag(tag); + updateSuggestedTags(currentFilter, SUGGESTED_LIMIT, false); + checkNoAddedTags(); + checkNoTagResults(); + } + private void updateSuggestedTags(String filter, int limit, boolean clearPrevious) { + UpdateSuggestedTagsTask task = new UpdateSuggestedTagsTask( + filter, + limit, + addedTagsAdapter, + suggestedTagsAdapter, + clearPrevious, + true, new UpdateSuggestedTagsTask.KnownTagsHandler() { + @Override + public void onSuccess(List tags) { + if (suggestedTagsAdapter == null) { + suggestedTagsAdapter = new TagListAdapter(tags, getContext()); + suggestedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD); + suggestedTagsAdapter.setClickListener(PublishFormFragment.this); + if (suggestedTagsList != null) { + suggestedTagsList.setAdapter(suggestedTagsAdapter); + } + } else { + suggestedTagsAdapter.setTags(tags); + } + + checkNoAddedTags(); + checkNoTagResults(); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + public void onWalletBalanceUpdated(WalletBalance walletBalance) { + if (walletBalance != null && inlineDepositBalanceValue != null) { + inlineDepositBalanceValue.setText(Helper.shortCurrencyFormat(walletBalance.getAvailable().doubleValue())); + } + } + + private void setupInlineChannelCreator( + View container, + TextInputEditText inputChannelName, + TextInputEditText inputDeposit, + View inlineBalanceView, + TextView inlineBalanceValue, + View linkCancel, + MaterialButton buttonCreate, + View progressView) { + inputDeposit.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View view, boolean hasFocus) { + Helper.setViewVisibility(inlineBalanceView, hasFocus ? View.VISIBLE : View.INVISIBLE); + } + }); + + linkCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Helper.setViewText(inputChannelName, null); + Helper.setViewText(inputDeposit, null); + Helper.setViewVisibility(container, View.GONE); + } + }); + + buttonCreate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // validate deposit and channel name + String channelNameString = Helper.normalizeChannelName(Helper.getValue(inputChannelName.getText())); + Claim claimToSave = new Claim(); + claimToSave.setName(channelNameString); + String channelName = claimToSave.getName().startsWith("@") ? claimToSave.getName().substring(1) : claimToSave.getName(); + String depositString = Helper.getValue(inputDeposit.getText()); + if ("@".equals(channelName) || Helper.isNullOrEmpty(channelName)) { + showError(getString(R.string.please_enter_channel_name)); + return; + } + if (!LbryUri.isNameValid(channelName)) { + showError(getString(R.string.channel_name_invalid_characters)); + return; + } + if (Helper.channelExists(channelName)) { + showError(getString(R.string.channel_name_already_created)); + return; + } + + double depositAmount = 0; + try { + depositAmount = Double.valueOf(depositString); + } catch (NumberFormatException ex) { + // pass + showError(getString(R.string.please_enter_valid_deposit)); + return; + } + if (depositAmount == 0) { + String error = getResources().getQuantityString(R.plurals.min_deposit_required, depositAmount == 1 ? 1 : 2, String.valueOf(Helper.MIN_DEPOSIT)); + showError(error); + return; + } + if (Lbry.walletBalance == null || Lbry.walletBalance.getAvailable().doubleValue() < depositAmount) { + showError(getString(R.string.deposit_more_than_balance)); + return; + } + + ChannelCreateUpdateTask task = new ChannelCreateUpdateTask( + claimToSave, new BigDecimal(depositString), false, progressView, new ClaimResultHandler() { + @Override + public void beforeStart() { + Helper.setViewEnabled(inputChannelName, false); + Helper.setViewEnabled(inputDeposit, false); + Helper.setViewEnabled(buttonCreate, false); + Helper.setViewEnabled(linkCancel, false); + } + + @Override + public void onSuccess(Claim claimResult) { + if (!BuildConfig.DEBUG) { + LogPublishTask logPublishTask = new LogPublishTask(claimResult); + logPublishTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + // channel created + Bundle bundle = new Bundle(); + bundle.putString("claim_id", claimResult.getClaimId()); + bundle.putString("claim_name", claimResult.getName()); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_CHANNEL_CREATE, bundle); + + // add the claim to the channel list and set it as the selected item + channelSpinnerAdapter.add(claimResult); + channelSpinner.setSelection(channelSpinnerAdapter.getCount() - 1); + + Helper.setViewEnabled(inputChannelName, true); + Helper.setViewEnabled(inputDeposit, true); + Helper.setViewEnabled(buttonCreate, true); + Helper.setViewEnabled(linkCancel, true); + } + + @Override + public void onError(Exception error) { + Helper.setViewEnabled(inputChannelName, true); + Helper.setViewEnabled(inputDeposit, true); + Helper.setViewEnabled(buttonCreate, true); + Helper.setViewEnabled(linkCancel, true); + showError(error.getMessage()); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + + Helper.setViewText(inlineBalanceValue, Helper.shortCurrencyFormat(Lbry.walletBalance.getAvailable().doubleValue())); + } + + private void showError(String message) { + Context context = getContext(); + if (context != null) { + Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).show(); + } + } + + @Override + public void onStoragePermissionGranted() { + + } + + @Override + public void onStoragePermissionRefused() { + + } } diff --git a/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java b/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java index 48c45bb3..91e386da 100644 --- a/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/publish/PublishFragment.java @@ -29,7 +29,9 @@ import com.google.android.material.snackbar.Snackbar; import com.google.common.util.concurrent.ListenableFuture; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import io.lbry.browser.MainActivity; @@ -38,9 +40,11 @@ import io.lbry.browser.adapter.GalleryGridAdapter; import io.lbry.browser.listener.CameraPermissionListener; import io.lbry.browser.listener.StoragePermissionListener; import io.lbry.browser.model.GalleryItem; +import io.lbry.browser.model.NavMenuItem; import io.lbry.browser.tasks.localdata.LoadGalleryItemsTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbry; import io.lbry.browser.utils.LbryAnalytics; public class PublishFragment extends BaseFragment implements CameraPermissionListener, StoragePermissionListener { @@ -123,8 +127,6 @@ public class PublishFragment extends BaseFragment implements CameraPermissionLis Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner) context, cameraSelector, preview); preview.setSurfaceProvider(cameraPreview.createSurfaceProvider(camera.getCameraInfo())); - } else { - android.util.Log.d("#HELP", "camera provider future is null?"); } } catch (ExecutionException | InterruptedException ex) { // pass @@ -220,6 +222,22 @@ public class PublishFragment extends BaseFragment implements CameraPermissionLis if (context != null) { if (adapter == null) { adapter = new GalleryGridAdapter(Arrays.asList(item), context); + adapter.setListener(new GalleryGridAdapter.GalleryItemClickListener() { + @Override + public void onGalleryItemClicked(GalleryItem item) { + if (!Lbry.SDK_READY) { + Snackbar.make(getView(), R.string.sdk_initializing_functionality, Snackbar.LENGTH_LONG).show(); + return; + } + + Context context = getContext(); + if (context instanceof MainActivity) { + Map params = new HashMap<>(); + params.put("galleryItem", item); + ((MainActivity) context).openFragment(PublishFormFragment.class, true, NavMenuItem.ID_ITEM_NEW_PUBLISH, params); + } + } + }); } else { adapter.addItem(item); } diff --git a/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java b/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java index 96d826b5..60edf552 100644 --- a/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/publish/PublishesFragment.java @@ -34,7 +34,6 @@ import io.lbry.browser.adapter.ClaimListAdapter; import io.lbry.browser.listener.SdkStatusListener; import io.lbry.browser.listener.SelectionModeListener; import io.lbry.browser.model.Claim; -import io.lbry.browser.tasks.claim.AbandonChannelTask; import io.lbry.browser.tasks.claim.AbandonHandler; import io.lbry.browser.tasks.claim.AbandonStreamTask; import io.lbry.browser.tasks.claim.ClaimListResultHandler; diff --git a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java index 779ab613..50fb3cfd 100644 --- a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java @@ -15,8 +15,6 @@ import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.exoplayer2.offline.Download; - import org.json.JSONException; import org.json.JSONObject; @@ -30,7 +28,7 @@ import io.lbry.browser.model.Claim; import io.lbry.browser.model.ClaimCacheKey; import io.lbry.browser.model.LbryFile; import io.lbry.browser.tasks.claim.ClaimListResultHandler; -import io.lbry.browser.tasks.claim.ClaimSearchTask; +import io.lbry.browser.tasks.claim.ClaimSearchResultHandler; import io.lbry.browser.tasks.LighthouseSearchTask; import io.lbry.browser.tasks.claim.ResolveTask; import io.lbry.browser.ui.BaseFragment; @@ -232,7 +230,7 @@ public class SearchFragment extends BaseFragment implements } LighthouseSearchTask task = new LighthouseSearchTask( - currentQuery, PAGE_SIZE, currentFrom, canShowMatureContent, null, loadingView, new ClaimSearchTask.ClaimSearchResultHandler() { + currentQuery, PAGE_SIZE, currentFrom, canShowMatureContent, null, loadingView, new ClaimSearchResultHandler() { @Override public void onSuccess(List claims, boolean hasReachedEnd) { contentHasReachedEnd = hasReachedEnd; diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java index 1a0656eb..59618f21 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java @@ -470,15 +470,12 @@ public class InvitesFragment extends BaseFragment implements SdkStatusListener, @Override public void onClick(View view) { // validate deposit and channel name - String channelNameString = Helper.getValue(inputChannelName.getText()); - if (!channelNameString.startsWith("@")) { - channelNameString = String.format("@%s", channelNameString); - } + String channelNameString = Helper.normalizeChannelName(Helper.getValue(inputChannelName.getText())); Claim claimToSave = new Claim(); claimToSave.setName(channelNameString); String channelName = claimToSave.getName().startsWith("@") ? claimToSave.getName().substring(1) : claimToSave.getName(); String depositString = Helper.getValue(inputDeposit.getText()); - if (Helper.isNullOrEmpty(channelName)) { + if ("@".equals(channelName) || Helper.isNullOrEmpty(channelName)) { showError(getString(R.string.please_enter_channel_name)); return; } diff --git a/app/src/main/java/io/lbry/browser/utils/Helper.java b/app/src/main/java/io/lbry/browser/utils/Helper.java index 19e428a3..1eb96d98 100644 --- a/app/src/main/java/io/lbry/browser/utils/Helper.java +++ b/app/src/main/java/io/lbry/browser/utils/Helper.java @@ -690,13 +690,20 @@ public final class Helper { return (int) (value * scale + 0.5f); } + public static String generateUrl() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + sb.append(Predefined.ADJECTIVES.get(random.nextInt(Predefined.ADJECTIVES.size()))).append("-"). + append(Predefined.ADJECTIVES.get(random.nextInt(Predefined.ADJECTIVES.size()))).append("-"). + append(Predefined.ANIMALS.get(random.nextInt(Predefined.ANIMALS.size()))); + return sb.toString().toLowerCase(); + } + public static void refreshRecyclerView(RecyclerView rv) { if (rv == null) { - android.util.Log.d("#HELP", "rv is null?"); return; } - android.util.Log.d("#HELP", "Refereshing recycler view..."); RecyclerView.Adapter adapter = rv.getAdapter(); int prevScrollPosition = 0; diff --git a/app/src/main/java/io/lbry/browser/utils/Lbry.java b/app/src/main/java/io/lbry/browser/utils/Lbry.java index 1e6e2c9f..e272d943 100644 --- a/app/src/main/java/io/lbry/browser/utils/Lbry.java +++ b/app/src/main/java/io/lbry/browser/utils/Lbry.java @@ -93,6 +93,7 @@ public final class Lbry { public static final String METHOD_CLAIM_LIST = "claim_list"; + public static final String METHOD_PURCHASE_LIST = "purchase_list"; public static final String METHOD_STREAM_ABANDON = "stream_abandon"; public static final String METHOD_STREAM_REPOST = "stream_repost"; diff --git a/app/src/main/java/io/lbry/browser/utils/Predefined.java b/app/src/main/java/io/lbry/browser/utils/Predefined.java index b6783523..bca81608 100644 --- a/app/src/main/java/io/lbry/browser/utils/Predefined.java +++ b/app/src/main/java/io/lbry/browser/utils/Predefined.java @@ -489,4 +489,1362 @@ public final class Predefined { "covid-19" ); public static final List MATURE_TAGS = Arrays.asList("mature", "nsfw", "porn", "xxx"); + public static final List ADJECTIVES = Arrays.asList( + "aback", + "abaft", + "abandoned", + "abashed", + "aberrant", + "abhorrent", + "abiding", + "abject", + "ablaze", + "able", + "abnormal", + "aboard", + "aboriginal", + "abortive", + "abounding", + "abrasive", + "abrupt", + "absent", + "absorbed", + "absorbing", + "abstracted", + "absurd", + "abundant", + "abusive", + "acceptable", + "accessible", + "accidental", + "accurate", + "acid", + "acidic", + "acoustic", + "acrid", + "actually", + "ad", + "hoc", + "adamant", + "adaptable", + "addicted", + "adhesive", + "adjoining", + "adorable", + "adventurous", + "afraid", + "aggressive", + "agonizing", + "agreeable", + "ahead", + "ajar", + "alcoholic", + "alert", + "alike", + "alive", + "alleged", + "alluring", + "aloof", + "amazing", + "ambiguous", + "ambitious", + "amuck", + "amused", + "amusing", + "ancient", + "angry", + "animated", + "annoyed", + "annoying", + "anxious", + "apathetic", + "aquatic", + "aromatic", + "arrogant", + "ashamed", + "aspiring", + "assorted", + "astonishing", + "attractive", + "auspicious", + "automatic", + "available", + "average", + "awake", + "aware", + "awesome", + "awful", + "axiomatic", + "bad", + "barbarous", + "bashful", + "bawdy", + "beautiful", + "befitting", + "belligerent", + "beneficial", + "bent", + "berserk", + "best", + "better", + "bewildered", + "big", + "billowy", + "bite-sized", + "bitter", + "bizarre", + "black", + "black-and-white", + "bloody", + "blue", + "blue-eyed", + "blushing", + "boiling", + "boorish", + "bored", + "boring", + "bouncy", + "boundless", + "brainy", + "brash", + "brave", + "brawny", + "breakable", + "breezy", + "brief", + "bright", + "bright", + "broad", + "broken", + "brown", + "bumpy", + "burly", + "bustling", + "busy", + "cagey", + "calculating", + "callous", + "calm", + "capable", + "capricious", + "careful", + "careless", + "caring", + "cautious", + "ceaseless", + "certain", + "changeable", + "charming", + "cheap", + "cheerful", + "chemical", + "chief", + "childlike", + "chilly", + "chivalrous", + "chubby", + "chunky", + "clammy", + "classy", + "clean", + "clear", + "clever", + "cloistered", + "cloudy", + "closed", + "clumsy", + "cluttered", + "coherent", + "cold", + "colorful", + "colossal", + "combative", + "comfortable", + "common", + "complete", + "complex", + "concerned", + "condemned", + "confused", + "conscious", + "cooing", + "cool", + "cooperative", + "coordinated", + "courageous", + "cowardly", + "crabby", + "craven", + "crazy", + "creepy", + "crooked", + "crowded", + "cruel", + "cuddly", + "cultured", + "cumbersome", + "curious", + "curly", + "curved", + "curvy", + "cut", + "cute", + "cute", + "cynical", + "daffy", + "daily", + "damaged", + "damaging", + "damp", + "dangerous", + "dapper", + "dark", + "dashing", + "dazzling", + "dead", + "deadpan", + "deafening", + "dear", + "debonair", + "decisive", + "decorous", + "deep", + "deeply", + "defeated", + "defective", + "defiant", + "delicate", + "delicious", + "delightful", + "demonic", + "delirious", + "dependent", + "depressed", + "deranged", + "descriptive", + "deserted", + "detailed", + "determined", + "devilish", + "didactic", + "different", + "difficult", + "diligent", + "direful", + "dirty", + "disagreeable", + "disastrous", + "discreet", + "disgusted", + "disgusting", + "disillusioned", + "dispensable", + "distinct", + "disturbed", + "divergent", + "dizzy", + "domineering", + "doubtful", + "drab", + "draconian", + "dramatic", + "dreary", + "drunk", + "dry", + "dull", + "dusty", + "dynamic", + "dysfunctional", + "eager", + "early", + "earsplitting", + "earthy", + "easy", + "eatable", + "economic", + "educated", + "efficacious", + "efficient", + "eight", + "elastic", + "elated", + "elderly", + "electric", + "elegant", + "elfin", + "elite", + "embarrassed", + "eminent", + "empty", + "enchanted", + "enchanting", + "encouraging", + "endurable", + "energetic", + "enormous", + "entertaining", + "enthusiastic", + "envious", + "equable", + "equal", + "erect", + "erratic", + "ethereal", + "evanescent", + "evasive", + "even", + "excellent", + "excited", + "exciting", + "exclusive", + "exotic", + "expensive", + "extra-large", + "extra-small", + "exuberant", + "exultant", + "fabulous", + "faded", + "faint", + "fair", + "faithful", + "fallacious", + "false", + "familiar", + "famous", + "fanatical", + "fancy", + "fantastic", + "far", + "far-flung", + "fascinated", + "fast", + "fat", + "faulty", + "fearful", + "fearless", + "feeble", + "feigned", + "female", + "fertile", + "festive", + "few", + "fierce", + "filthy", + "fine", + "finicky", + "first", + "five", + "fixed", + "flagrant", + "flaky", + "flashy", + "flat", + "flawless", + "flimsy", + "flippant", + "flowery", + "fluffy", + "fluttering", + "foamy", + "foolish", + "foregoing", + "forgetful", + "fortunate", + "four", + "frail", + "fragile", + "frantic", + "free", + "freezing", + "frequent", + "fresh", + "fretful", + "friendly", + "frightened", + "frightening", + "full", + "fumbling", + "functional", + "funny", + "furry", + "furtive", + "future", + "futuristic", + "fuzzy", + "gabby", + "gainful", + "gamy", + "gaping", + "garrulous", + "gaudy", + "general", + "gentle", + "giant", + "giddy", + "gifted", + "gigantic", + "glamorous", + "gleaming", + "glib", + "glistening", + "glorious", + "glossy", + "godly", + "good", + "goofy", + "gorgeous", + "graceful", + "grandiose", + "grateful", + "gratis", + "gray", + "greasy", + "great", + "greedy", + "green", + "grey", + "grieving", + "groovy", + "grotesque", + "grouchy", + "grubby", + "gruesome", + "grumpy", + "guarded", + "guiltless", + "gullible", + "gusty", + "guttural", + "habitual", + "half", + "hallowed", + "halting", + "handsome", + "handsomely", + "handy", + "hanging", + "hapless", + "happy", + "hard", + "hard-to-find", + "harmonious", + "harsh", + "hateful", + "heady", + "healthy", + "heartbreaking", + "heavenly", + "heavy", + "hellish", + "helpful", + "helpless", + "hesitant", + "hideous", + "high", + "highfalutin", + "high-pitched", + "hilarious", + "hissing", + "historical", + "holistic", + "hollow", + "homeless", + "homely", + "honorable", + "horrible", + "hospitable", + "hot", + "huge", + "hulking", + "humdrum", + "humorous", + "hungry", + "hurried", + "hurt", + "hushed", + "husky", + "hypnotic", + "hysterical", + "icky", + "icy", + "idiotic", + "ignorant", + "ill", + "illegal", + "ill-fated", + "ill-informed", + "illustrious", + "imaginary", + "immense", + "imminent", + "impartial", + "imperfect", + "impolite", + "important", + "imported", + "impossible", + "incandescent", + "incompetent", + "inconclusive", + "industrious", + "incredible", + "inexpensive", + "infamous", + "innate", + "innocent", + "inquisitive", + "insidious", + "instinctive", + "intelligent", + "interesting", + "internal", + "invincible", + "irate", + "irritating", + "itchy", + "jaded", + "jagged", + "jazzy", + "jealous", + "jittery", + "jobless", + "jolly", + "joyous", + "judicious", + "juicy", + "jumbled", + "jumpy", + "juvenile", + "kaput", + "keen", + "kind", + "kindhearted", + "kindly", + "knotty", + "knowing", + "knowledgeable", + "known", + "labored", + "lackadaisical", + "lacking", + "lame", + "lamentable", + "languid", + "large", + "last", + "late", + "laughable", + "lavish", + "lazy", + "lean", + "learned", + "left", + "legal", + "lethal", + "level", + "lewd", + "light", + "like", + "likeable", + "limping", + "literate", + "little", + "lively", + "lively", + "living", + "lonely", + "long", + "longing", + "long-term", + "loose", + "lopsided", + "loud", + "loutish", + "lovely", + "loving", + "low", + "lowly", + "lucky", + "ludicrous", + "lumpy", + "lush", + "luxuriant", + "lying", + "lyrical", + "macabre", + "macho", + "maddening", + "madly", + "magenta", + "magical", + "magnificent", + "majestic", + "makeshift", + "male", + "malicious", + "mammoth", + "maniacal", + "many", + "marked", + "massive", + "married", + "marvelous", + "material", + "materialistic", + "mature", + "mean", + "measly", + "meaty", + "medical", + "meek", + "mellow", + "melodic", + "melted", + "merciful", + "mere", + "messy", + "mighty", + "military", + "milky", + "mindless", + "miniature", + "minor", + "miscreant", + "misty", + "mixed", + "moaning", + "modern", + "moldy", + "momentous", + "motionless", + "mountainous", + "muddled", + "mundane", + "murky", + "mushy", + "mute", + "mysterious", + "naive", + "nappy", + "narrow", + "nasty", + "natural", + "naughty", + "nauseating", + "near", + "neat", + "nebulous", + "necessary", + "needless", + "needy", + "neighborly", + "nervous", + "new", + "next", + "nice", + "nifty", + "nimble", + "nine", + "nippy", + "noiseless", + "noisy", + "nonchalant", + "nondescript", + "nonstop", + "normal", + "nostalgic", + "nosy", + "noxious", + "null", + "numberless", + "numerous", + "nutritious", + "nutty", + "oafish", + "obedient", + "obeisant", + "obese", + "obnoxious", + "obscene", + "obsequious", + "observant", + "obsolete", + "obtainable", + "oceanic", + "odd", + "offbeat", + "old", + "old-fashioned", + "omniscient", + "one", + "onerous", + "open", + "opposite", + "optimal", + "orange", + "ordinary", + "organic", + "ossified", + "outgoing", + "outrageous", + "outstanding", + "oval", + "overconfident", + "overjoyed", + "overrated", + "overt", + "overwrought", + "painful", + "painstaking", + "pale", + "paltry", + "panicky", + "panoramic", + "parallel", + "parched", + "parsimonious", + "past", + "pastoral", + "pathetic", + "peaceful", + "penitent", + "perfect", + "periodic", + "permissible", + "perpetual", + "petite", + "petite", + "phobic", + "physical", + "picayune", + "pink", + "piquant", + "placid", + "plain", + "plant", + "plastic", + "plausible", + "pleasant", + "plucky", + "pointless", + "poised", + "polite", + "political", + "poor", + "possessive", + "possible", + "powerful", + "precious", + "premium", + "present", + "pretty", + "previous", + "pricey", + "prickly", + "private", + "probable", + "productive", + "profuse", + "protective", + "proud", + "psychedelic", + "psychotic", + "public", + "puffy", + "pumped", + "puny", + "purple", + "purring", + "pushy", + "puzzled", + "puzzling", + "quack", + "quaint", + "quarrelsome", + "questionable", + "quick", + "quickest", + "quiet", + "quirky", + "quixotic", + "quizzical", + "rabid", + "racial", + "ragged", + "rainy", + "rambunctious", + "rampant", + "rapid", + "rare", + "raspy", + "ratty", + "ready", + "real", + "rebel", + "receptive", + "recondite", + "red", + "redundant", + "reflective", + "regular", + "relieved", + "remarkable", + "reminiscent", + "repulsive", + "resolute", + "resonant", + "responsible", + "rhetorical", + "rich", + "right", + "righteous", + "rightful", + "rigid", + "ripe", + "ritzy", + "roasted", + "robust", + "romantic", + "roomy", + "rotten", + "rough", + "round", + "royal", + "ruddy", + "rude", + "rural", + "rustic", + "ruthless", + "sable", + "sad", + "safe", + "salty", + "same", + "sassy", + "satisfying", + "savory", + "scandalous", + "scarce", + "scared", + "scary", + "scattered", + "scientific", + "scintillating", + "scrawny", + "screeching", + "second", + "second-hand", + "secret", + "secretive", + "sedate", + "seemly", + "selective", + "selfish", + "separate", + "serious", + "shaggy", + "shaky", + "shallow", + "sharp", + "shiny", + "shivering", + "shocking", + "short", + "shrill", + "shut", + "shy", + "sick", + "silent", + "silent", + "silky", + "silly", + "simple", + "simplistic", + "sincere", + "six", + "skillful", + "skinny", + "sleepy", + "slim", + "slimy", + "slippery", + "sloppy", + "slow", + "small", + "smart", + "smelly", + "smiling", + "smoggy", + "smooth", + "sneaky", + "snobbish", + "snotty", + "soft", + "soggy", + "solid", + "somber", + "sophisticated", + "sordid", + "sore", + "sore", + "sour", + "sparkling", + "special", + "spectacular", + "spicy", + "spiffy", + "spiky", + "spiritual", + "spiteful", + "splendid", + "spooky", + "spotless", + "spotted", + "spotty", + "spurious", + "squalid", + "square", + "squealing", + "squeamish", + "staking", + "stale", + "standing", + "statuesque", + "steadfast", + "steady", + "steep", + "stereotyped", + "sticky", + "stiff", + "stimulating", + "stingy", + "stormy", + "straight", + "strange", + "striped", + "strong", + "stupendous", + "stupid", + "sturdy", + "subdued", + "subsequent", + "substantial", + "successful", + "succinct", + "sudden", + "sulky", + "super", + "superb", + "superficial", + "supreme", + "swanky", + "sweet", + "sweltering", + "swift", + "symptomatic", + "synonymous", + "taboo", + "tacit", + "tacky", + "talented", + "tall", + "tame", + "tan", + "tangible", + "tangy", + "tart", + "tasteful", + "tasteless", + "tasty", + "tawdry", + "tearful", + "tedious", + "teeny", + "teeny-tiny", + "telling", + "temporary", + "ten", + "tender", + "tense", + "tense", + "tenuous", + "terrible", + "terrific", + "tested", + "testy", + "thankful", + "therapeutic", + "thick", + "thin", + "thinkable", + "third", + "thirsty", + "thoughtful", + "thoughtless", + "threatening", + "three", + "thundering", + "tidy", + "tight", + "tightfisted", + "tiny", + "tired", + "tiresome", + "toothsome", + "torpid", + "tough", + "towering", + "tranquil", + "trashy", + "tremendous", + "tricky", + "trite", + "troubled", + "truculent", + "true", + "truthful", + "two", + "typical", + "ubiquitous", + "ugliest", + "ugly", + "ultra", + "unable", + "unaccountable", + "unadvised", + "unarmed", + "unbecoming", + "unbiased", + "uncovered", + "understood", + "undesirable", + "unequal", + "unequaled", + "uneven", + "unhealthy", + "uninterested", + "unique", + "unkempt", + "unknown", + "unnatural", + "unruly", + "unsightly", + "unsuitable", + "untidy", + "unused", + "unusual", + "unwieldy", + "unwritten", + "upbeat", + "uppity", + "upset", + "uptight", + "used", + "useful", + "useless", + "utopian", + "utter", + "uttermost", + "vacuous", + "vagabond", + "vague", + "valuable", + "various", + "vast", + "vengeful", + "venomous", + "verdant", + "versed", + "victorious", + "vigorous", + "violent", + "violet", + "vivacious", + "voiceless", + "volatile", + "voracious", + "vulgar", + "wacky", + "waggish", + "waiting", + "wakeful", + "wandering", + "wanting", + "warlike", + "warm", + "wary", + "wasteful", + "watery", + "weak", + "wealthy", + "weary", + "well-groomed", + "well-made", + "well-off", + "well-to-do", + "wet", + "whimsical", + "whispering", + "white", + "whole", + "wholesale", + "wicked", + "wide", + "wide-eyed", + "wiggly", + "wild", + "willing", + "windy", + "wiry", + "wise", + "wistful", + "witty", + "woebegone", + "womanly", + "wonderful", + "wooden", + "woozy", + "workable", + "worried", + "worthless", + "wrathful", + "wretched", + "wrong", + "wry", + "xenophobic", + "yellow", + "yielding", + "young", + "youthful", + "yummy", + "zany", + "zealous", + "zesty", + "zippy", + "zonked" + ); + public static final List ANIMALS = Arrays.asList( + "Aardvark", + "Albatross", + "Alligator", + "Alpaca", + "Ant", + "Anteater", + "Antelope", + "Ape", + "Armadillo", + "Donkey", + "Baboon", + "Badger", + "Barracuda", + "Bat", + "Bear", + "Beaver", + "Bee", + "Bison", + "Boar", + "Buffalo", + "Butterfly", + "Camel", + "Capybara", + "Caribou", + "Cassowary", + "Cat", + "Caterpillar", + "Cattle", + "Chamois", + "Cheetah", + "Chicken", + "Chimpanzee", + "Chinchilla", + "Chough", + "Clam", + "Cobra", + "Cockroach", + "Cod", + "Cormorant", + "Coyote", + "Crab", + "Crane", + "Crocodile", + "Crow", + "Curlew", + "Deer", + "Dinosaur", + "Dog", + "Dogfish", + "Dolphin", + "Dotterel", + "Dove", + "Dragonfly", + "Duck", + "Dugong", + "Dunlin", + "Eagle", + "Echidna", + "Eel", + "Eland", + "Elephant", + "Elk", + "Emu", + "Falcon", + "Ferret", + "Finch", + "Fish", + "Flamingo", + "Fly", + "Fox", + "Frog", + "Gaur", + "Gazelle", + "Gerbil", + "Giraffe", + "Gnat", + "Gnu", + "Goat", + "Goldfinch", + "Goldfish", + "Goose", + "Gorilla", + "Goshawk", + "Grasshopper", + "Grouse", + "Guanaco", + "Gull", + "Hamster", + "Hare", + "Hawk", + "Hedgehog", + "Heron", + "Herring", + "Hippopotamus", + "Hornet", + "Horse", + "Human", + "Hummingbird", + "Hyena", + "Ibex", + "Ibis", + "Jackal", + "Jaguar", + "Jay", + "Jellyfish", + "Kangaroo", + "Kingfisher", + "Koala", + "Kookabura", + "Kouprey", + "Kudu", + "Lapwing", + "Lark", + "Lemur", + "Leopard", + "Lion", + "Llama", + "Lobster", + "Locust", + "Loris", + "Louse", + "Lyrebird", + "Magpie", + "Mallard", + "Manatee", + "Mandrill", + "Mantis", + "Marten", + "Meerkat", + "Mink", + "Mole", + "Mongoose", + "Monkey", + "Moose", + "Mosquito", + "Mouse", + "Mule", + "Narwhal", + "Newt", + "Nightingale", + "Octopus", + "Okapi", + "Opossum", + "Oryx", + "Ostrich", + "Otter", + "Owl", + "Oyster", + "Panther", + "Parrot", + "Partridge", + "Peafowl", + "Pelican", + "Penguin", + "Pheasant", + "Pig", + "Pigeon", + "Pony", + "Porcupine", + "Porpoise", + "Quail", + "Quelea", + "Quetzal", + "Rabbit", + "Raccoon", + "Rail", + "Ram", + "Rat", + "Raven", + "Reindeer", + "Rhinoceros", + "Rook", + "Salamander", + "Salmon", + "Sandpiper", + "Sardine", + "Scorpion", + "Seahorse", + "Seal", + "Shark", + "Sheep", + "Shrew", + "Skunk", + "Snail", + "Snake", + "Sparrow", + "Spider", + "Spoonbill", + "Squid", + "Squirrel", + "Starling", + "Stingray", + "Stinkbug", + "Stork", + "Swallow", + "Swan", + "Tapir", + "Tarsier", + "Termite", + "Tiger", + "Toad", + "Trout", + "Turkey", + "Turtle", + "Viper", + "Vulture", + "Wallaby", + "Walrus", + "Wasp", + "Weasel", + "Whale", + "Wildcat", + "Wolf", + "Wolverine", + "Wombat", + "Woodcock", + "Woodpecker", + "Worm", + "Wren", + "Yak", + "Zebra" + ); } diff --git a/app/src/main/res/layout/container_inline_channel_form.xml b/app/src/main/res/layout/container_inline_channel_form.xml index bdacd126..adad2a95 100644 --- a/app/src/main/res/layout/container_inline_channel_form.xml +++ b/app/src/main/res/layout/container_inline_channel_form.xml @@ -32,10 +32,11 @@ diff --git a/app/src/main/res/layout/container_inline_tag_form.xml b/app/src/main/res/layout/container_inline_tag_form.xml index ec0279a7..bdd32a3c 100644 --- a/app/src/main/res/layout/container_inline_tag_form.xml +++ b/app/src/main/res/layout/container_inline_tag_form.xml @@ -33,7 +33,7 @@ android:id="@+id/form_mature_tags_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="16dp" + android:layout_marginTop="8dp" android:orientation="vertical" android:visibility="gone"> @@ -21,6 +23,7 @@ android:id="@+id/url_suggestions_container" android:background="@color/pageBackground" android:elevation="6dp" + android:fitsSystemWindows="true" android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/fragment_channel_form.xml b/app/src/main/res/layout/fragment_channel_form.xml index b3c5a20c..006a1724 100644 --- a/app/src/main/res/layout/fragment_channel_form.xml +++ b/app/src/main/res/layout/fragment_channel_form.xml @@ -123,6 +123,7 @@ android:layout_height="match_parent" android:orientation="vertical"> + diff --git a/app/src/main/res/layout/fragment_publish_form.xml b/app/src/main/res/layout/fragment_publish_form.xml index c49b0712..48ba5905 100644 --- a/app/src/main/res/layout/fragment_publish_form.xml +++ b/app/src/main/res/layout/fragment_publish_form.xml @@ -4,76 +4,73 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/pageBackground"> - - - - + + android:layout_height="240dp" + android:background="@android:color/black"> - - - + + - + android:layout_marginTop="4dp" + android:orientation="horizontal" + android:paddingTop="2dp" + android:paddingBottom="2dp" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:visibility="gone"> + + + - - + + + - - - + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6660284a..3a19f1be 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,7 +102,62 @@ LBRY requires access to be able to display and publish your videos, images and other files from your device. LBRY requires access to your camera to record videos. LBRY requires access to your camera to take photos. + Edit content Mature tags + Price + Your content will be free. Press the toggle to set a price. + Content address + Address + The address where people can find your content (ex. lbry://myvideo) + License + License description + Additional Options + Show extra fields + Hide extra fields + No file found to publish. + You cannot publish content right now beceause the background service is still initializing. + + Language + English + Chinese + French + German + Japanese + Russian + Spanish + Indonesian + Italian + Dutch + Turkish + Polish + Malay + Portuguese + Vietnamese + Thai + Arabic + Czech + Croatian + Cambodian + Korean + Norwegian + Romanian + Hindi + Greek + + None + Public Domain + Copyright + Creative Commons Attribution 4.0 International + Creative Commons Attribution-ShareAlike 4.0 International + Creative Commons Attribution-NoDerivatives 4.0 International + Creative Commons Attribution-NonCommercial 4.0 International + Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International + + + LBC + USD + One or more content items could not be deleted at this time. Please try again later. @@ -174,7 +229,7 @@ Receive Credits Use this wallet address to receive credits sent by another user (or yourself). Get new address - You can generate a new address at any time, and any previous addresses will conintue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources. + You can generate a new address at any time, and any previous addresses will continue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources. Send Credits Recipient address @@ -253,6 +308,7 @@ You have not followed any tags yet. Get started by adding tags that you are interested in! We could not find new tags that you\'re not following. The \'%1$s\' tag has already been added. + You cannot add more than 5 tags. Send a tip Send a tip to %1$s This will appear as a tip for %1$s, which will boost its ability to be discovered while active. <a href="https://lbry.com/faq/tipping">Learn more</a>. @@ -264,7 +320,7 @@ Show advanced Hide advanced Name - 0.01 + 0.01 The content was successfully reposted! The repost name contains invalid characters. @@ -317,8 +373,6 @@ Description Yes No - Show extra - Hide extra Show optional fields Hide optional fields Save @@ -403,9 +457,11 @@ Downloads + Purchases History You have not downloaded any content to this device. You have not viewed any content on this device. + You have not purchased any content on your account. Hide Stats Video