Finish Following and All Content views. Add customize your tags view.
This commit is contained in:
parent
59584c1be7
commit
1d1c761d3f
41 changed files with 1761 additions and 169 deletions
|
@ -5,6 +5,7 @@ import android.content.BroadcastReceiver;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
@ -18,6 +19,7 @@ import android.widget.TextView;
|
|||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.widget.NestedScrollView;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
@ -292,12 +294,14 @@ public class FileViewActivity extends AppCompatActivity {
|
|||
TagListAdapter tagListAdapter = new TagListAdapter(tags, this);
|
||||
tagListAdapter.setClickListener(new TagListAdapter.TagClickListener() {
|
||||
@Override
|
||||
public void onTagClicked(Tag tag) {
|
||||
public void onTagClicked(Tag tag, int customizeMode) {
|
||||
if (customizeMode == TagListAdapter.CUSTOMIZE_MODE_NONE) {
|
||||
Intent intent = new Intent(MainActivity.ACTION_OPEN_ALL_CONTENT_TAG);
|
||||
intent.putExtra("tag", tag.getName());
|
||||
sendBroadcast(intent);
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
descTagsList.setAdapter(tagListAdapter);
|
||||
findViewById(R.id.file_view_tag_area).setVisibility(tags.size() > 0 ? View.VISIBLE : View.GONE);
|
||||
|
@ -378,7 +382,11 @@ public class FileViewActivity extends AppCompatActivity {
|
|||
String title = claim.getTitle();
|
||||
String claimId = claim.getClaimId();
|
||||
ProgressBar relatedLoading = findViewById(R.id.file_view_related_content_progress);
|
||||
LighthouseSearchTask relatedTask = new LighthouseSearchTask(title, RELATED_CONTENT_SIZE, 0, false, claimId, relatedLoading, new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
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() {
|
||||
@Override
|
||||
public void onSuccess(List<Claim> claims, boolean hasReachedEnd) {
|
||||
List<Claim> filteredClaims = new ArrayList<>();
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.content.IntentFilter;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
|
@ -20,7 +21,6 @@ import android.os.Handler;
|
|||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.Menu;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
@ -63,6 +63,7 @@ import java.io.InputStreamReader;
|
|||
import java.net.ConnectException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -73,12 +74,13 @@ import java.util.concurrent.TimeUnit;
|
|||
import io.lbry.browser.adapter.NavigationMenuAdapter;
|
||||
import io.lbry.browser.adapter.UrlSuggestionListAdapter;
|
||||
import io.lbry.browser.data.DatabaseHelper;
|
||||
import io.lbry.browser.exceptions.ApiCallException;
|
||||
import io.lbry.browser.exceptions.LbryUriException;
|
||||
import io.lbry.browser.listener.SdkStatusListener;
|
||||
import io.lbry.browser.listener.WalletBalanceListener;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.ClaimCacheKey;
|
||||
import io.lbry.browser.model.NavMenuItem;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.model.UrlSuggestion;
|
||||
import io.lbry.browser.model.WalletBalance;
|
||||
import io.lbry.browser.model.WalletSync;
|
||||
|
@ -87,7 +89,6 @@ import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
|||
import io.lbry.browser.tasks.ResolveTask;
|
||||
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
|
||||
import io.lbry.browser.tasks.wallet.SyncGetTask;
|
||||
import io.lbry.browser.tasks.wallet.SyncTaskHandler;
|
||||
import io.lbry.browser.tasks.wallet.WalletBalanceTask;
|
||||
import io.lbry.browser.ui.BaseFragment;
|
||||
import io.lbry.browser.ui.channel.ChannelFragment;
|
||||
|
@ -103,6 +104,7 @@ import io.lbry.browser.utils.Lbryio;
|
|||
import io.lbry.lbrysdk.LbrynetService;
|
||||
import io.lbry.lbrysdk.ServiceHelper;
|
||||
import io.lbry.lbrysdk.Utils;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements SdkStatusListener {
|
||||
|
@ -141,6 +143,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
|
||||
// preference keys
|
||||
public static final String PREFERENCE_KEY_DARK_MODE = "io.lbry.browser.preference.userinterface.DarkMode";
|
||||
public static final String PREFERENCE_KEY_SHOW_MATURE_CONTENT = "io.lbry.browser.preference.userinterface.ShowMatureContent";
|
||||
public static final String PREFERENCE_KEY_NOTIFICATION_URL_SUGGESTIONS = "io.lbry.browser.preference.userinterface.UrlSuggestions";
|
||||
public static final String PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS = "io.lbry.browser.preference.notifications.Subscriptions";
|
||||
public static final String PREFERENCE_KEY_NOTIFICATION_REWARDS = "io.lbry.browser.preference.notifications.Rewards";
|
||||
|
@ -164,6 +167,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
|
||||
private NavigationMenuAdapter navMenuAdapter;
|
||||
private UrlSuggestionListAdapter urlSuggestionListAdapter;
|
||||
private List<UrlSuggestion> recentHistory;
|
||||
|
||||
// broadcast receivers
|
||||
private BroadcastReceiver serviceActionsReceiver;
|
||||
|
@ -526,10 +530,18 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
private void toggleUrlSuggestions(boolean visible) {
|
||||
View container = findViewById(R.id.url_suggestions_container);
|
||||
View closeIcon = findViewById(R.id.wunderbar_close);
|
||||
EditText wunderbar = findViewById(R.id.wunderbar);
|
||||
wunderbar.setPadding(0, 0, visible ? getScaledValue(36) : 0, 0);
|
||||
|
||||
container.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
closeIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private int getScaledValue(int value) {
|
||||
float scale = getResources().getDisplayMetrics().density;
|
||||
return (int) (value * scale + 0.5f);
|
||||
}
|
||||
|
||||
private void setupUriBar() {
|
||||
findViewById(R.id.wunderbar_close).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -543,6 +555,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
@Override
|
||||
public void onFocusChange(View view, boolean hasFocus) {
|
||||
toggleUrlSuggestions(hasFocus);
|
||||
if (hasFocus && Helper.isNullOrEmpty(Helper.getValue(((EditText) view).getText()))) {
|
||||
displayUrlSuggestionsForNoInput();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -572,6 +587,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
switch (urlSuggestion.getType()) {
|
||||
case UrlSuggestion.TYPE_CHANNEL:
|
||||
// open channel page
|
||||
openChannelUrl(urlSuggestion.getUri().toString());
|
||||
break;
|
||||
case UrlSuggestion.TYPE_FILE:
|
||||
Context context = MainActivity.this;
|
||||
|
@ -586,6 +602,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
break;
|
||||
case UrlSuggestion.TYPE_TAG:
|
||||
// open tag page
|
||||
openAllContentFragmentWithTag(urlSuggestion.getText());
|
||||
break;
|
||||
}
|
||||
findViewById(R.id.wunderbar).clearFocus();
|
||||
|
@ -618,6 +635,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
ResolveTask task = new ResolveTask(urls, Lbry.LBRY_TV_CONNECTION_STRING, null, new ResolveTask.ResolveResultHandler() {
|
||||
@Override
|
||||
public void onSuccess(List<Claim> claims) {
|
||||
if (findViewById(R.id.url_suggestions_container).getVisibility() == View.VISIBLE) {
|
||||
for (int i = 0; i < claims.size(); i++) {
|
||||
// build a simple url from the claim for matching
|
||||
Claim claim = claims.get(i);
|
||||
|
@ -637,6 +655,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
}
|
||||
urlSuggestionListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
|
@ -646,10 +665,19 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void displayUrlSuggestionsForNoInput() {
|
||||
urlSuggestionListAdapter.clear();
|
||||
List<UrlSuggestion> blankSuggestions = buildDefaultSuggestionsForBlankUrl();
|
||||
urlSuggestionListAdapter.addUrlSuggestions(blankSuggestions);
|
||||
List<String> urls = urlSuggestionListAdapter.getItemUrls();
|
||||
resolveUrlSuggestions(urls);
|
||||
}
|
||||
|
||||
private void handleUriInputChanged(String text) {
|
||||
// build the default suggestions
|
||||
urlSuggestionListAdapter.clear();
|
||||
if (Helper.isNullOrEmpty(text)) {
|
||||
if (Helper.isNullOrEmpty(text) || text.trim().equals("@")) {
|
||||
displayUrlSuggestionsForNoInput();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -659,11 +687,13 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
LighthouseAutoCompleteTask task = new LighthouseAutoCompleteTask(text, null, new LighthouseAutoCompleteTask.AutoCompleteResultHandler() {
|
||||
@Override
|
||||
public void onSuccess(List<UrlSuggestion> suggestions) {
|
||||
String wunderBarText = Helper.getValue(((EditText) findViewById(R.id.wunderbar)).getText());
|
||||
if (wunderBarText.equalsIgnoreCase(text)) {
|
||||
urlSuggestionListAdapter.addUrlSuggestions(suggestions);
|
||||
|
||||
List<String> urls = urlSuggestionListAdapter.getItemUrls();
|
||||
resolveUrlSuggestions(urls);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
|
@ -673,12 +703,37 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private List<UrlSuggestion> buildDefaultSuggestionsForBlankUrl() {
|
||||
List<UrlSuggestion> suggestions = new ArrayList<>();
|
||||
if (recentHistory != null && recentHistory.size() > 0) {
|
||||
// show recent history if avaiable
|
||||
suggestions = new ArrayList<>(recentHistory);
|
||||
} else {
|
||||
try {
|
||||
suggestions.add(new UrlSuggestion(
|
||||
UrlSuggestion.TYPE_FILE, "What is LBRY?", LbryUri.parse("lbry://what#19b9c243bea0c45175e6a6027911abbad53e983e")));
|
||||
suggestions.add(new UrlSuggestion(
|
||||
UrlSuggestion.TYPE_CHANNEL, "LBRYCast", LbryUri.parse("lbry://@lbrycast#4c29f8b013adea4d5cca1861fb2161d5089613ea")));
|
||||
suggestions.add(new UrlSuggestion(
|
||||
UrlSuggestion.TYPE_CHANNEL, "The LBRY Channel", LbryUri.parse("lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a")));
|
||||
for (UrlSuggestion suggestion : suggestions) {
|
||||
suggestion.setUseTextAsDescription(true);
|
||||
}
|
||||
} catch (LbryUriException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
private List<UrlSuggestion> buildDefaultSuggestions(String text) {
|
||||
List<UrlSuggestion> suggestions = new ArrayList<UrlSuggestion>();
|
||||
|
||||
// First item is always search
|
||||
if (!text.startsWith(LbryUri.PROTO_DEFAULT)) {
|
||||
UrlSuggestion searchSuggestion = new UrlSuggestion(UrlSuggestion.TYPE_SEARCH, text);
|
||||
suggestions.add(searchSuggestion);
|
||||
}
|
||||
|
||||
if (!text.matches(LbryUri.REGEX_INVALID_URI)) {
|
||||
boolean isChannel = text.startsWith("@");
|
||||
|
@ -990,6 +1045,12 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
return false;
|
||||
}
|
||||
|
||||
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||
List<Tag> fetchedTags = DatabaseHelper.getTags(db);
|
||||
Lbry.knownTags = Helper.mergeKnownTags(fetchedTags);
|
||||
Collections.sort(Lbry.knownTags, new Tag());
|
||||
Lbry.followedTags = Helper.filterFollowedTags(Lbry.knownTags);
|
||||
|
||||
// load the exchange rate
|
||||
if (Lbryio.LBCUSDRate == 0) {
|
||||
Lbryio.loadExchangeRate();
|
||||
|
@ -1175,7 +1236,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
findContentGroup.setItems(Arrays.asList(
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_FOLLOWING, R.string.fa_heart, R.string.following, "Following", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_EDITORS_CHOICE, R.string.fa_star, R.string.editors_choice, "EditorsChoice", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_YOUR_TAGS, R.string.fa_hashtag, R.string.your_tags, "YourTags", context),
|
||||
new NavMenuItem(NavMenuItem.ID_ITEM_ALL_CONTENT, R.string.fa_globe_americas, R.string.all_content, "AllContent", context)
|
||||
));
|
||||
|
||||
|
@ -1421,4 +1481,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
|||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void loadTags() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -13,23 +14,35 @@ import java.util.List;
|
|||
|
||||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHolder> {
|
||||
|
||||
public static final int CUSTOMIZE_MODE_NONE = 0;
|
||||
public static final int CUSTOMIZE_MODE_ADD = 1;
|
||||
public static final int CUSTOMIZE_MODE_REMOVE = 2;
|
||||
|
||||
private Context context;
|
||||
private List<Tag> items;
|
||||
@Setter
|
||||
private TagClickListener clickListener;
|
||||
@Getter
|
||||
@Setter
|
||||
private int customizeMode;
|
||||
|
||||
public TagListAdapter(List<Tag> tags, Context context) {
|
||||
this.context = context;
|
||||
this.items = new ArrayList<>(tags);
|
||||
this.customizeMode = CUSTOMIZE_MODE_NONE;
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
protected ImageView iconView;
|
||||
protected TextView nameView;
|
||||
public ViewHolder(View v) {
|
||||
super(v);
|
||||
iconView = v.findViewById(R.id.tag_action);
|
||||
nameView = v.findViewById(R.id.tag_name);
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +51,21 @@ public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHold
|
|||
return items != null ? items.size() : 0;
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
if (!items.contains(tag)) {
|
||||
items.add(tag);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
public List<Tag> getTags() {
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
items = new ArrayList<>(tags);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void addTags(List<Tag> tags) {
|
||||
for (Tag tag : tags) {
|
||||
if (!items.contains(tag)) {
|
||||
|
@ -46,6 +74,10 @@ public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHold
|
|||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
public void removeTag(Tag tag) {
|
||||
items.remove(tag);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagListAdapter.ViewHolder onCreateViewHolder(ViewGroup root, int viewType) {
|
||||
|
@ -57,18 +89,20 @@ public class TagListAdapter extends RecyclerView.Adapter<TagListAdapter.ViewHold
|
|||
public void onBindViewHolder(TagListAdapter.ViewHolder vh, int position) {
|
||||
Tag tag = items.get(position);
|
||||
vh.nameView.setText(tag.getName().toLowerCase());
|
||||
vh.iconView.setVisibility(customizeMode == CUSTOMIZE_MODE_NONE ? View.GONE : View.VISIBLE);
|
||||
vh.iconView.setImageResource(customizeMode == CUSTOMIZE_MODE_REMOVE ? R.drawable.ic_close : R.drawable.ic_add);
|
||||
vh.itemView.setBackgroundResource(tag.isMature() ? R.drawable.bg_tag_mature : R.drawable.bg_tag);
|
||||
vh.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (clickListener != null) {
|
||||
clickListener.onTagClicked(tag);
|
||||
clickListener.onTagClicked(tag, customizeMode);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface TagClickListener {
|
||||
void onTagClicked(Tag tag);
|
||||
void onTagClicked(Tag tag, int customizeMode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.exceptions.LbryUriException;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.UrlSuggestion;
|
||||
import io.lbry.browser.ui.controls.SolidIconView;
|
||||
|
@ -52,10 +53,17 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
|
|||
public void setClaimForUrl(LbryUri url, Claim claim) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
LbryUri thisUrl = items.get(i).getUri();
|
||||
if (thisUrl != null && thisUrl.equals(url)) {
|
||||
try {
|
||||
if (thisUrl != null) {
|
||||
LbryUri vanity = LbryUri.parse(thisUrl.toVanityString());
|
||||
if (thisUrl.equals(url) || vanity.equals(url)) {
|
||||
items.get(i).setClaim(claim);
|
||||
}
|
||||
}
|
||||
} catch (LbryUriException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addUrlSuggestions(List<UrlSuggestion> urlSuggestions) {
|
||||
|
@ -88,7 +96,7 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
|
|||
iconStringId = R.string.fa_at;
|
||||
fullTitle = item.getTitle();
|
||||
desc = item.getClaim() != null ? item.getClaim().getTitle() :
|
||||
String.format(context.getString(R.string.view_channel_url_desc), item.getText());
|
||||
(item.isUseTextAsDescription() ? item.getText() : String.format(context.getString(R.string.view_channel_url_desc), item.getText()));
|
||||
break;
|
||||
case UrlSuggestion.TYPE_TAG:
|
||||
iconStringId = R.string.fa_hashtag;
|
||||
|
@ -105,7 +113,7 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
|
|||
iconStringId = R.string.fa_file;
|
||||
fullTitle = item.getTitle();
|
||||
desc = item.getClaim() != null ? item.getClaim().getTitle() :
|
||||
String.format(context.getString(R.string.view_file_url_desc), item.getText());
|
||||
(item.isUseTextAsDescription() ? item.getText() : String.format(context.getString(R.string.view_file_url_desc), item.getText()));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,17 @@ import android.database.Cursor;
|
|||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import io.lbry.browser.exceptions.LbryUriException;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.model.UrlSuggestion;
|
||||
import io.lbry.browser.model.lbryinc.Subscription;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import io.lbry.browser.utils.LbryUri;
|
||||
|
||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||
public static final int DATABASE_VERSION = 1;
|
||||
|
@ -18,18 +24,34 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
private static final String[] SQL_CREATE_TABLES = {
|
||||
// local subscription store
|
||||
"CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL)",
|
||||
|
||||
// local claim cache store for quick load / refresh
|
||||
// url entry / suggestion history
|
||||
"CREATE TABLE history (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL, url TEXT, type INTEGER NOT NULL, timestamp TEXT NOT NULL)",
|
||||
// tags (known and followed)
|
||||
"CREATE TABLE tags (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, is_followed INTEGER NOT NULL)"
|
||||
// local claim cache store for quick load / refresh (or offline mode)?
|
||||
|
||||
};
|
||||
private static final String[] SQL_CREATE_INDEXES = {
|
||||
"CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)"
|
||||
"CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)",
|
||||
"CREATE UNIQUE INDEX idx_history_value ON history (value)",
|
||||
"CREATE UNIQUE INDEX idx_history_url ON history (url)",
|
||||
"CREATE UNIQUE INDEX idx_tag_name ON tags (name)"
|
||||
};
|
||||
|
||||
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url) VALUES (?, ?)";
|
||||
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?";
|
||||
private static final String SQL_GET_SUBSCRIPTIONS = "SELECT channel_name, url FROM subscriptions";
|
||||
|
||||
private static final String SQL_INSERT_HISTORY = "REPLACE INTO history (value, url, type, timestamp) VALUES (?, ?, ?)";
|
||||
private static final String SQL_CLEAR_HISTORY = "DELETE FROM history";
|
||||
private static final String SQL_CLEAR_HISTORY_BEFORE_TIME = "DELETE FROM history WHERE timestamp < ?";
|
||||
private static final String SQL_GET_RECENT_HISTORY = "SELECT value, url, type FROM history ORDER BY timestamp DESC LIMIT 10";
|
||||
|
||||
private static final String SQL_INSERT_TAG = "REPLACE INTO tags (name, is_followed) VALUES (?, ?)";
|
||||
private static final String SQL_SET_TAG_FOLLOWED = "UPDATE tags SET is_followed = ? WHERE name = ?";
|
||||
private static final String SQL_GET_KNOWN_TAGS = "SELECT name FROM tags";
|
||||
private static final String SQL_GET_FOLLOWED_TAGS = "SELECT name FROM tags WHERE is_followed = 1";
|
||||
|
||||
public DatabaseHelper(Context context) {
|
||||
super(context, String.format("%s/%s", context.getFilesDir().getAbsolutePath(), DATABASE_NAME), null, DATABASE_VERSION);
|
||||
}
|
||||
|
@ -48,6 +70,64 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
|||
|
||||
}
|
||||
|
||||
public static void createOrUpdateHistoryItem(String text, String url, int type, SQLiteDatabase db) {
|
||||
db.execSQL(SQL_INSERT_HISTORY, new Object[] {
|
||||
text, url, type, new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(new Date())
|
||||
});
|
||||
}
|
||||
public static void clearHistory(SQLiteDatabase db) {
|
||||
db.execSQL(SQL_CLEAR_HISTORY);
|
||||
}
|
||||
public static void clearHistoryBefore(Date date, SQLiteDatabase db) {
|
||||
db.execSQL(SQL_CLEAR_HISTORY_BEFORE_TIME, new Object[] { new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(new Date()) });
|
||||
}
|
||||
// History items are essentially url suggestions
|
||||
public static List<UrlSuggestion> getRecentHistory(SQLiteDatabase db) {
|
||||
List<UrlSuggestion> suggestions = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = db.rawQuery(SQL_GET_RECENT_HISTORY, null);
|
||||
while (cursor.moveToNext()) {
|
||||
UrlSuggestion suggestion = new UrlSuggestion();
|
||||
suggestion.setText(cursor.getString(0));
|
||||
suggestion.setType(cursor.getInt(2));
|
||||
|
||||
try {
|
||||
suggestion.setUri(cursor.isNull(1) ? null : LbryUri.parse(cursor.getString(1)));
|
||||
} catch (LbryUriException ex) {
|
||||
// don't fail if the LbryUri is invalid
|
||||
}
|
||||
suggestions.add(suggestion);
|
||||
}
|
||||
} finally {
|
||||
Helper.closeCursor(cursor);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
public static void createOrUpdateTag(Tag tag, SQLiteDatabase db) {
|
||||
db.execSQL(SQL_INSERT_TAG, new Object[] { tag.getLowercaseName(), tag.isFollowed() ? 1 : 0 });
|
||||
}
|
||||
public static void setTagFollowed(boolean followed, String name, SQLiteDatabase db) {
|
||||
db.execSQL(SQL_SET_TAG_FOLLOWED, new Object[] { followed ? 1 : 0, name });
|
||||
}
|
||||
public static List<Tag> getTags(SQLiteDatabase db) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = db.rawQuery(SQL_GET_KNOWN_TAGS, null);
|
||||
while (cursor.moveToNext()) {
|
||||
Tag tag = new Tag();
|
||||
tag.setName(cursor.getString(0));
|
||||
tag.setFollowed(cursor.getInt(1) == 1);
|
||||
tags.add(tag);
|
||||
}
|
||||
} finally {
|
||||
Helper.closeCursor(cursor);
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) {
|
||||
db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] { subscription.getChannelName(), subscription.getUrl() });
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package io.lbry.browser.dialog;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import io.lbry.browser.R;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ContentScopeDialogFragment extends BottomSheetDialogFragment {
|
||||
public static final String TAG = "ContentScopeDialog";
|
||||
public static final int ITEM_EVERYONE = 1;
|
||||
public static final int ITEM_TAGS = 2;
|
||||
|
||||
@Setter
|
||||
private ContentScopeListener contentScopeListener;
|
||||
private int currentScopeItem;
|
||||
|
||||
public static ContentScopeDialogFragment newInstance() {
|
||||
return new ContentScopeDialogFragment();
|
||||
}
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_content_scope, container,false);
|
||||
|
||||
ContentScopeItemClickListener clickListener = new ContentScopeItemClickListener(this, contentScopeListener);
|
||||
view.findViewById(R.id.content_scope_everyone_item).setOnClickListener(clickListener);
|
||||
view.findViewById(R.id.content_scope_tags_item).setOnClickListener(clickListener);
|
||||
checkSelectedScopeItem(currentScopeItem, view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public static void checkSelectedScopeItem(int scope, View parent) {
|
||||
int checkViewId = -1;
|
||||
switch (scope) {
|
||||
case ITEM_EVERYONE: checkViewId = R.id.content_scope_everyone_item_selected; break;
|
||||
case ITEM_TAGS: checkViewId = R.id.content_scope_tags_item_selected; break;
|
||||
}
|
||||
if (parent != null && checkViewId > -1) {
|
||||
parent.findViewById(checkViewId).setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentScopeItem(int scopeItem) {
|
||||
this.currentScopeItem = scopeItem;
|
||||
}
|
||||
|
||||
private static class ContentScopeItemClickListener implements View.OnClickListener {
|
||||
|
||||
private final int[] checkViewIds = {
|
||||
R.id.content_scope_everyone_item_selected, R.id.content_scope_tags_item_selected
|
||||
};
|
||||
private BottomSheetDialogFragment dialog;
|
||||
private ContentScopeListener listener;
|
||||
|
||||
public ContentScopeItemClickListener(BottomSheetDialogFragment dialog, ContentScopeListener listener) {
|
||||
this.dialog = dialog;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
int scopeItem = -1;
|
||||
|
||||
if (dialog != null) {
|
||||
View dialogView = dialog.getView();
|
||||
if (dialogView != null) {
|
||||
for (int id : checkViewIds) {
|
||||
dialogView.findViewById(id).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (view.getId()) {
|
||||
case R.id.content_scope_everyone_item: scopeItem = ITEM_EVERYONE; break;
|
||||
case R.id.content_scope_tags_item: scopeItem = ITEM_TAGS; break;
|
||||
}
|
||||
|
||||
checkSelectedScopeItem(scopeItem, view);
|
||||
if (listener != null) {
|
||||
listener.onContentScopeItemSelected(scopeItem);
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ContentScopeListener {
|
||||
void onContentScopeItemSelected(int scopeItem);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
package io.lbry.browser.dialog;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.adapter.TagListAdapter;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import io.lbry.browser.utils.Lbry;
|
||||
import lombok.Setter;
|
||||
|
||||
public class CustomizeTagsDialogFragment extends BottomSheetDialogFragment {
|
||||
public static final String TAG = "CustomizeTagsDialog";
|
||||
private static final int SUGGESTED_LIMIT = 8;
|
||||
private String currentFilter;
|
||||
|
||||
private RecyclerView followedTagsList;
|
||||
private RecyclerView suggestedTagsList;
|
||||
private TagListAdapter followedTagsAdapter;
|
||||
private TagListAdapter suggestedTagsAdapter;
|
||||
private View noTagsView;
|
||||
private View noResultsView;
|
||||
@Setter
|
||||
private TagListener listener;
|
||||
|
||||
private void checkNoTags() {
|
||||
Helper.setViewVisibility(noTagsView, followedTagsAdapter == null || followedTagsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
private void checkNoResults() {
|
||||
Helper.setViewVisibility(noResultsView, suggestedTagsAdapter == null || suggestedTagsAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
public void addTag(Tag tag) {
|
||||
if (followedTagsAdapter.getTags().contains(tag)) {
|
||||
Snackbar.make(getView(), getString(R.string.tag_already_followed, tag.getName()), Snackbar.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
tag.setFollowed(true);
|
||||
followedTagsAdapter.addTag(tag);
|
||||
if (suggestedTagsAdapter != null) {
|
||||
suggestedTagsAdapter.removeTag(tag);
|
||||
}
|
||||
updateKnownTags(currentFilter, SUGGESTED_LIMIT, false);
|
||||
if (listener != null) {
|
||||
listener.onTagAdded(tag);
|
||||
}
|
||||
checkNoTags();
|
||||
checkNoResults();
|
||||
}
|
||||
public void removeTag(Tag tag) {
|
||||
tag.setFollowed(false);
|
||||
followedTagsAdapter.removeTag(tag);
|
||||
updateKnownTags(currentFilter, SUGGESTED_LIMIT, false);
|
||||
if (listener != null) {
|
||||
listener.onTagRemoved(tag);
|
||||
}
|
||||
checkNoTags();
|
||||
checkNoResults();
|
||||
}
|
||||
|
||||
public void setFilter(String filter) {
|
||||
currentFilter = filter;
|
||||
updateKnownTags(currentFilter, SUGGESTED_LIMIT, true);
|
||||
}
|
||||
|
||||
public static CustomizeTagsDialogFragment newInstance() {
|
||||
return new CustomizeTagsDialogFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_customize_tags, container, false);
|
||||
|
||||
noResultsView = view.findViewById(R.id.customize_no_tag_results);
|
||||
noTagsView = view.findViewById(R.id.customize_no_followed_tags);
|
||||
|
||||
followedTagsAdapter = new TagListAdapter(Lbry.followedTags, getContext());
|
||||
followedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_REMOVE);
|
||||
followedTagsAdapter.setClickListener(customizeTagClickListener);
|
||||
suggestedTagsAdapter = new TagListAdapter(new ArrayList<>(), getContext());
|
||||
suggestedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD);
|
||||
suggestedTagsAdapter.setClickListener(customizeTagClickListener);
|
||||
|
||||
FlexboxLayoutManager flm1 = new FlexboxLayoutManager(getContext());
|
||||
followedTagsList = view.findViewById(R.id.customize_tags_followed_list);
|
||||
followedTagsList.setLayoutManager(flm1);
|
||||
followedTagsList.setAdapter(followedTagsAdapter);
|
||||
|
||||
FlexboxLayoutManager flm2 = new FlexboxLayoutManager(getContext());
|
||||
suggestedTagsList = view.findViewById(R.id.customize_tags_suggested_list);
|
||||
suggestedTagsList.setLayoutManager(flm2);
|
||||
suggestedTagsList.setAdapter(suggestedTagsAdapter);
|
||||
|
||||
TextInputEditText filterInput = view.findViewById(R.id.customize_tag_filter_input);
|
||||
filterInput.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) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
MaterialButton doneButton = view.findViewById(R.id.customize_done_button);
|
||||
doneButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
checkNoTags();
|
||||
return view;
|
||||
}
|
||||
|
||||
private TagListAdapter.TagClickListener customizeTagClickListener = new TagListAdapter.TagClickListener() {
|
||||
@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 onResume() {
|
||||
super.onResume();
|
||||
updateKnownTags(null, SUGGESTED_LIMIT, true);
|
||||
}
|
||||
|
||||
private void updateKnownTags(String filter, int limit, boolean clearPrevious) {
|
||||
(new AsyncTask<Void, Void, List<Tag>>() {
|
||||
protected List<Tag> doInBackground(Void... params) {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
if (Helper.isNullOrEmpty(filter)) {
|
||||
Random random = new Random();
|
||||
if (suggestedTagsAdapter != null && !clearPrevious) {
|
||||
tags = new ArrayList<>(suggestedTagsAdapter.getTags());
|
||||
}
|
||||
while (tags.size() < limit) {
|
||||
Tag randomTag = Lbry.knownTags.get(random.nextInt(Lbry.knownTags.size()));
|
||||
if (!Lbry.followedTags.contains(randomTag) && (followedTagsAdapter == null || !followedTagsAdapter.getTags().contains(randomTag))) {
|
||||
tags.add(randomTag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Tag filterTag = new Tag(filter);
|
||||
if (followedTagsAdapter == null || !followedTagsAdapter.getTags().contains(filterTag)) {
|
||||
tags.add(new Tag(filter));
|
||||
}
|
||||
for (int i = 0; i < Lbry.knownTags.size() && tags.size() < SUGGESTED_LIMIT - 1; i++) {
|
||||
Tag knownTag = Lbry.knownTags.get(i);
|
||||
if ((knownTag.getLowercaseName().startsWith(filter) || knownTag.getLowercaseName().matches(filter)) &&
|
||||
(!tags.contains(knownTag) &&
|
||||
!Lbry.followedTags.contains(knownTag) && (followedTagsAdapter == null || !followedTagsAdapter.getTags().contains(knownTag)))) {
|
||||
tags.add(knownTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
protected void onPostExecute(List<Tag> tags) {
|
||||
if (suggestedTagsAdapter == null) {
|
||||
suggestedTagsAdapter = new TagListAdapter(tags, getContext());
|
||||
suggestedTagsAdapter.setCustomizeMode(TagListAdapter.CUSTOMIZE_MODE_ADD);
|
||||
suggestedTagsAdapter.setClickListener(customizeTagClickListener);
|
||||
if (suggestedTagsList != null) {
|
||||
suggestedTagsList.setAdapter(suggestedTagsAdapter);
|
||||
}
|
||||
} else {
|
||||
suggestedTagsAdapter.setTags(tags);
|
||||
}
|
||||
checkNoResults();
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
public interface TagListener {
|
||||
void onTagAdded(Tag tag);
|
||||
void onTagRemoved(Tag tag);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package io.lbry.browser.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ClaimSearchCacheValue {
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Claim> claims;
|
||||
@Getter
|
||||
@Setter
|
||||
private long timestamp;
|
||||
|
||||
public ClaimSearchCacheValue(List<Claim> claims, long timestamp) {
|
||||
this.claims = new ArrayList<>(claims);
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public boolean isExpired(long ttl) {
|
||||
return System.currentTimeMillis() - timestamp > ttl;
|
||||
}
|
||||
}
|
|
@ -19,8 +19,7 @@ public class NavMenuItem {
|
|||
// Find Content
|
||||
public static final int ID_ITEM_FOLLOWING = 101;
|
||||
public static final int ID_ITEM_EDITORS_CHOICE = 102;
|
||||
public static final int ID_ITEM_YOUR_TAGS = 103;
|
||||
public static final int ID_ITEM_ALL_CONTENT = 104;
|
||||
public static final int ID_ITEM_ALL_CONTENT = 103;
|
||||
|
||||
// Your Content
|
||||
public static final int ID_ITEM_CHANNELS = 201;
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package io.lbry.browser.model;
|
||||
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import java.util.Comparator;
|
||||
|
||||
import io.lbry.browser.utils.Predefined;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class Tag {
|
||||
public class Tag implements Comparator<Tag> {
|
||||
@Getter
|
||||
@Setter
|
||||
private String name;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean followed;
|
||||
|
||||
public Tag() {
|
||||
|
||||
}
|
||||
public Tag(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -18,13 +26,16 @@ public class Tag {
|
|||
}
|
||||
|
||||
public boolean isMature() {
|
||||
return Helper.MATURE_TAG_NAMES.contains(name.toLowerCase());
|
||||
return Predefined.MATURE_TAGS.contains(name.toLowerCase());
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Tag) && ((Tag) o).getName().equalsIgnoreCase(name);
|
||||
}
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
return name.toLowerCase().hashCode();
|
||||
}
|
||||
public int compare(Tag a, Tag b) {
|
||||
return a.getLowercaseName().compareToIgnoreCase(b.getLowercaseName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,21 +14,36 @@ public class UrlSuggestion {
|
|||
private String text;
|
||||
private LbryUri uri;
|
||||
private Claim claim; // associated claim if resolved
|
||||
private boolean titleTextOnly;
|
||||
private boolean useTextAsDescription;
|
||||
|
||||
public UrlSuggestion() {
|
||||
|
||||
}
|
||||
public UrlSuggestion(int type, String text) {
|
||||
this.type = type;
|
||||
this.text = text;
|
||||
}
|
||||
public UrlSuggestion(int type, String text, LbryUri uri) {
|
||||
this(type, text);
|
||||
this.uri = uri;
|
||||
}
|
||||
public UrlSuggestion(int type, String text, LbryUri uri, boolean titleTextOnly) {
|
||||
this(type, text, uri);
|
||||
this.titleTextOnly = titleTextOnly;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
if (!titleTextOnly) {
|
||||
switch (type) {
|
||||
case TYPE_CHANNEL:
|
||||
return String.format("%s - %s", text.startsWith("@") ? text.substring(1) : text, uri.toString());
|
||||
return String.format("%s - %s", text.startsWith("@") ? text.substring(1) : text, uri.toVanityString());
|
||||
case TYPE_FILE:
|
||||
return String.format("%s - %s", text, uri.toString());
|
||||
return String.format("%s - %s", text, uri.toVanityString());
|
||||
case TYPE_TAG:
|
||||
return String.format("%s - #%s", text, text);
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
|
59
app/src/main/java/io/lbry/browser/tasks/LoadTagsTask.java
Normal file
59
app/src/main/java/io/lbry/browser/tasks/LoadTagsTask.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package io.lbry.browser.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.data.DatabaseHelper;
|
||||
import io.lbry.browser.exceptions.LbryRequestException;
|
||||
import io.lbry.browser.exceptions.LbryResponseException;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
|
||||
public class LoadTagsTask extends AsyncTask<Void, Void, List<Tag>> {
|
||||
private Context context;
|
||||
private LoadTagsHandler handler;
|
||||
private Exception error;
|
||||
|
||||
public LoadTagsTask(Context context, LoadTagsHandler handler) {
|
||||
this.context = context;
|
||||
this.handler = handler;
|
||||
}
|
||||
protected List<Tag> doInBackground(Void... params) {
|
||||
List<Tag> tags = null;
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
if (context instanceof MainActivity) {
|
||||
db = ((MainActivity) context).getDbHelper().getReadableDatabase();
|
||||
if (db != null) {
|
||||
tags = DatabaseHelper.getTags(db);
|
||||
}
|
||||
}
|
||||
} catch (SQLiteException ex) {
|
||||
error = ex;
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
protected void onPostExecute(List<Tag> tags) {
|
||||
if (handler != null) {
|
||||
if (tags != null) {
|
||||
handler.onSuccess(tags);
|
||||
} else {
|
||||
handler.onError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface LoadTagsHandler {
|
||||
void onSuccess(List<Tag> tags);
|
||||
void onError(Exception error);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package io.lbry.browser.ui.allcontent;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -10,9 +11,12 @@ import android.view.ViewGroup;
|
|||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -23,19 +27,24 @@ import io.lbry.browser.MainActivity;
|
|||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.adapter.ClaimListAdapter;
|
||||
import io.lbry.browser.dialog.ContentFromDialogFragment;
|
||||
import io.lbry.browser.dialog.ContentScopeDialogFragment;
|
||||
import io.lbry.browser.dialog.ContentSortDialogFragment;
|
||||
import io.lbry.browser.dialog.CustomizeTagsDialogFragment;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.tasks.ClaimSearchTask;
|
||||
import io.lbry.browser.ui.BaseFragment;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import io.lbry.browser.utils.Lbry;
|
||||
import io.lbry.browser.utils.Predefined;
|
||||
|
||||
// TODO: Similar code to FollowingFragment and Channel page fragment. Probably make common operations (sorting/filtering) into a control
|
||||
public class AllContentFragment extends BaseFragment {
|
||||
public class AllContentFragment extends BaseFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private boolean singleTagView;
|
||||
private List<String> tags;
|
||||
private View layoutFilterContainer;
|
||||
private View customizeLink;
|
||||
private View sortLink;
|
||||
private View contentFromLink;
|
||||
private View scopeLink;
|
||||
|
@ -46,13 +55,14 @@ public class AllContentFragment extends BaseFragment {
|
|||
private RecyclerView contentList;
|
||||
private int currentSortBy;
|
||||
private int currentContentFrom;
|
||||
private int currentScope;
|
||||
private int currentContentScope;
|
||||
private String contentReleaseTime;
|
||||
private List<String> contentSortOrder;
|
||||
private View fromPrefix;
|
||||
private View forPrefix;
|
||||
private View contentLoading;
|
||||
private View bigContentLoading;
|
||||
private View noContentView;
|
||||
private ClaimListAdapter contentListAdapter;
|
||||
private boolean contentClaimSearchLoading;
|
||||
private boolean contentHasReachedEnd;
|
||||
|
@ -72,6 +82,7 @@ public class AllContentFragment extends BaseFragment {
|
|||
sortLink = root.findViewById(R.id.all_content_sort_link);
|
||||
contentFromLink = root.findViewById(R.id.all_content_time_link);
|
||||
scopeLink = root.findViewById(R.id.all_content_scope_link);
|
||||
customizeLink = root.findViewById(R.id.all_content_customize_link);
|
||||
fromPrefix = root.findViewById(R.id.all_content_from_prefix);
|
||||
forPrefix = root.findViewById(R.id.all_content_for_prefix);
|
||||
|
||||
|
@ -81,6 +92,7 @@ public class AllContentFragment extends BaseFragment {
|
|||
|
||||
bigContentLoading = root.findViewById(R.id.all_content_main_progress);
|
||||
contentLoading = root.findViewById(R.id.all_content_load_progress);
|
||||
noContentView = root.findViewById(R.id.all_content_no_claim_search_content);
|
||||
|
||||
contentList = root.findViewById(R.id.all_content_list);
|
||||
LinearLayoutManager llm = new LinearLayoutManager(getContext());
|
||||
|
@ -127,6 +139,25 @@ public class AllContentFragment extends BaseFragment {
|
|||
}
|
||||
}
|
||||
});
|
||||
scopeLink.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ContentScopeDialogFragment dialog = ContentScopeDialogFragment.newInstance();
|
||||
dialog.setCurrentScopeItem(currentContentScope);
|
||||
dialog.setContentScopeListener(new ContentScopeDialogFragment.ContentScopeListener() {
|
||||
@Override
|
||||
public void onContentScopeItemSelected(int scopeItem) {
|
||||
onContentScopeChanged(scopeItem);
|
||||
}
|
||||
});
|
||||
|
||||
Context context = getContext();
|
||||
if (context instanceof MainActivity) {
|
||||
MainActivity activity = (MainActivity) context;
|
||||
dialog.show(activity.getSupportFragmentManager(), ContentScopeDialogFragment.TAG);
|
||||
}
|
||||
}
|
||||
});
|
||||
contentFromLink.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
@ -145,6 +176,12 @@ public class AllContentFragment extends BaseFragment {
|
|||
}
|
||||
}
|
||||
});
|
||||
customizeLink.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
showCustomizeTagsDialog();
|
||||
}
|
||||
});
|
||||
|
||||
checkParams(false);
|
||||
|
||||
|
@ -188,6 +225,51 @@ public class AllContentFragment extends BaseFragment {
|
|||
fetchClaimSearchContent(true);
|
||||
}
|
||||
|
||||
private void onContentScopeChanged(int contentScope) {
|
||||
currentContentScope = contentScope;
|
||||
|
||||
// rebuild options and search
|
||||
updateContentScopeLinkText();
|
||||
boolean isTagScope = currentContentScope == ContentScopeDialogFragment.ITEM_TAGS;
|
||||
if (isTagScope) {
|
||||
tags = Helper.getTagsForTagObjects(Lbry.followedTags);
|
||||
// Update tags list with the user's followed tags
|
||||
if (tags == null || tags.size() == 0) {
|
||||
Snackbar.make(getView(), R.string.customize_tags_hint, Snackbar.LENGTH_LONG).setAction(R.string.customize, new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
// show customize
|
||||
showCustomizeTagsDialog();
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
Helper.setViewVisibility(customizeLink, isTagScope ? View.VISIBLE : View.GONE);
|
||||
fetchClaimSearchContent(true);
|
||||
}
|
||||
|
||||
private void showCustomizeTagsDialog() {
|
||||
CustomizeTagsDialogFragment dialog = CustomizeTagsDialogFragment.newInstance();
|
||||
dialog.setListener(new CustomizeTagsDialogFragment.TagListener() {
|
||||
@Override
|
||||
public void onTagAdded(Tag tag) {
|
||||
// heavy-lifting
|
||||
// save to local, save to wallet and then sync
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTagRemoved(Tag tag) {
|
||||
// heavy-lifting
|
||||
// save to local, save to wallet and then sync
|
||||
}
|
||||
});
|
||||
Context context = getContext();
|
||||
if (context instanceof MainActivity) {
|
||||
MainActivity activity = (MainActivity) context;
|
||||
dialog.show(activity.getSupportFragmentManager(), CustomizeTagsDialogFragment.TAG);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSortByChanged(int sortBy) {
|
||||
currentSortBy = sortBy;
|
||||
|
||||
|
@ -214,6 +296,16 @@ public class AllContentFragment extends BaseFragment {
|
|||
Helper.setViewText(sortLinkText, stringResourceId);
|
||||
}
|
||||
|
||||
private void updateContentScopeLinkText() {
|
||||
int stringResourceId = -1;
|
||||
switch (currentContentScope) {
|
||||
case ContentScopeDialogFragment.ITEM_EVERYONE: default: stringResourceId = R.string.everyone; break;
|
||||
case ContentScopeDialogFragment.ITEM_TAGS: stringResourceId = R.string.tags; break;
|
||||
}
|
||||
|
||||
Helper.setViewText(scopeLinkText, stringResourceId);
|
||||
}
|
||||
|
||||
private void updateContentFromLinkText() {
|
||||
int stringResourceId = -1;
|
||||
switch (currentContentFrom) {
|
||||
|
@ -229,14 +321,26 @@ public class AllContentFragment extends BaseFragment {
|
|||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
updateContentFromLinkText();
|
||||
updateContentScopeLinkText();
|
||||
updateSortByLinkText();
|
||||
fetchClaimSearchContent();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private Map<String, Object> buildContentOptions() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
|
||||
return Lbry.buildClaimSearchOptions(
|
||||
Claim.TYPE_STREAM,
|
||||
tags != null ? tags : null,
|
||||
null, // TODO: Check mature
|
||||
(currentContentScope == ContentScopeDialogFragment.ITEM_EVERYONE) ? null : tags,
|
||||
canShowMatureContent ? null : new ArrayList<>(Predefined.MATURE_TAGS),
|
||||
null,
|
||||
null,
|
||||
getContentSortOrder(),
|
||||
|
@ -267,6 +371,7 @@ public class AllContentFragment extends BaseFragment {
|
|||
}
|
||||
|
||||
contentClaimSearchLoading = true;
|
||||
Helper.setViewVisibility(noContentView, View.GONE);
|
||||
Map<String, Object> claimSearchOptions = buildContentOptions();
|
||||
contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
@Override
|
||||
|
@ -303,13 +408,26 @@ public class AllContentFragment extends BaseFragment {
|
|||
|
||||
contentHasReachedEnd = hasReachedEnd;
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent();
|
||||
}
|
||||
});
|
||||
contentClaimSearchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void checkNoContent() {
|
||||
boolean noContent = contentListAdapter == null || contentListAdapter.getItemCount() == 0;
|
||||
Helper.setViewVisibility(noContentView, noContent ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
||||
fetchClaimSearchContent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -11,9 +12,11 @@ import android.widget.TextView;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -28,9 +31,10 @@ import io.lbry.browser.model.Claim;
|
|||
import io.lbry.browser.tasks.ClaimSearchTask;
|
||||
import io.lbry.browser.utils.Helper;
|
||||
import io.lbry.browser.utils.Lbry;
|
||||
import io.lbry.browser.utils.Predefined;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ChannelContentFragment extends Fragment {
|
||||
public class ChannelContentFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@Setter
|
||||
private String channelId;
|
||||
|
@ -45,6 +49,7 @@ public class ChannelContentFragment extends Fragment {
|
|||
private List<String> contentSortOrder;
|
||||
private View contentLoading;
|
||||
private View bigContentLoading;
|
||||
private View noContentView;
|
||||
private ClaimListAdapter contentListAdapter;
|
||||
private boolean contentClaimSearchLoading;
|
||||
private boolean contentHasReachedEnd;
|
||||
|
@ -66,6 +71,7 @@ public class ChannelContentFragment extends Fragment {
|
|||
|
||||
bigContentLoading = root.findViewById(R.id.channel_content_main_progress);
|
||||
contentLoading = root.findViewById(R.id.channel_content_load_progress);
|
||||
noContentView = root.findViewById(R.id.channel_content_no_claim_search_content);
|
||||
|
||||
contentList = root.findViewById(R.id.channel_content_list);
|
||||
LinearLayoutManager llm = new LinearLayoutManager(getContext());
|
||||
|
@ -183,18 +189,23 @@ public class ChannelContentFragment extends Fragment {
|
|||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
fetchClaimSearchContent();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
fetchClaimSearchContent(true);
|
||||
public void onPause() {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private Map<String, Object> buildContentOptions() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
|
||||
return Lbry.buildClaimSearchOptions(
|
||||
Claim.TYPE_STREAM,
|
||||
null,
|
||||
null, // TODO: Check mature
|
||||
canShowMatureContent ? null : new ArrayList<>(Predefined.MATURE_TAGS),
|
||||
Arrays.asList(channelId),
|
||||
null,
|
||||
getContentSortOrder(),
|
||||
|
@ -225,6 +236,7 @@ public class ChannelContentFragment extends Fragment {
|
|||
}
|
||||
|
||||
contentClaimSearchLoading = true;
|
||||
Helper.setViewVisibility(noContentView, View.GONE);
|
||||
Map<String, Object> claimSearchOptions = buildContentOptions();
|
||||
contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
@Override
|
||||
|
@ -261,13 +273,26 @@ public class ChannelContentFragment extends Fragment {
|
|||
|
||||
contentHasReachedEnd = hasReachedEnd;
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent();
|
||||
}
|
||||
});
|
||||
contentClaimSearchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private void checkNoContent() {
|
||||
boolean noContent = contentListAdapter == null || contentListAdapter.getItemCount() == 0;
|
||||
Helper.setViewVisibility(noContentView, noContent ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
||||
fetchClaimSearchContent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,6 @@ public class ChannelFragment extends BaseFragment {
|
|||
}
|
||||
}
|
||||
if (updateRequired) {
|
||||
resetFragments();
|
||||
if (!Helper.isNullOrEmpty(url)) {
|
||||
resolveUrl();
|
||||
} else if (claim == null) {
|
||||
|
@ -179,25 +178,6 @@ public class ChannelFragment extends BaseFragment {
|
|||
}).attach();
|
||||
}
|
||||
|
||||
private void resetFragments() {
|
||||
try {
|
||||
Context context = getContext();
|
||||
if (context instanceof MainActivity) {
|
||||
MainActivity activity = (MainActivity) getContext();
|
||||
FragmentManager manager = activity.getSupportFragmentManager();
|
||||
FragmentTransaction tx = manager.beginTransaction();
|
||||
for (Fragment fragment : manager.getFragments()) {
|
||||
if (fragment.getClass().equals(ChannelAboutFragment.class) || fragment.getClass().equals(ChannelContentFragment.class)) {
|
||||
tx.remove(fragment);
|
||||
}
|
||||
}
|
||||
tx.commitAllowingStateLoss();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChannelPagerAdapter extends FragmentStateAdapter {
|
||||
private Claim channelClaim;
|
||||
public ChannelPagerAdapter(Claim channelClaim, FragmentActivity activity) {
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -11,6 +12,8 @@ import android.widget.ProgressBar;
|
|||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
@ -45,8 +48,11 @@ import io.lbry.browser.utils.Helper;
|
|||
import io.lbry.browser.utils.Lbry;
|
||||
import io.lbry.browser.utils.LbryUri;
|
||||
import io.lbry.browser.utils.Lbryio;
|
||||
import io.lbry.browser.utils.Predefined;
|
||||
|
||||
public class FollowingFragment extends BaseFragment implements FetchSubscriptionsTask.FetchSubscriptionsHandler, ChannelItemSelectionListener {
|
||||
public class FollowingFragment extends BaseFragment implements
|
||||
FetchSubscriptionsTask.FetchSubscriptionsHandler,
|
||||
ChannelItemSelectionListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final int SUGGESTED_PAGE_SIZE = 45;
|
||||
private static final int MIN_SUGGESTED_SUBSCRIBE_COUNT = 5;
|
||||
|
@ -74,6 +80,7 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
private List<String> contentSortOrder;
|
||||
private boolean contentClaimSearchLoading = false;
|
||||
private boolean suggestedClaimSearchLoading = false;
|
||||
private View noContentView;
|
||||
|
||||
private List<Integer> queuedContentPages = new ArrayList<>();
|
||||
private List<Integer> queuedSuggestedPages = new ArrayList<>();
|
||||
|
@ -122,6 +129,7 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
contentLoading = root.findViewById(R.id.following_content_progress);
|
||||
channelListLoading = root.findViewById(R.id.following_channel_load_progress);
|
||||
discoverLink = root.findViewById(R.id.following_discover_link);
|
||||
noContentView = root.findViewById(R.id.following_no_claim_search_content);
|
||||
|
||||
Context context = getContext();
|
||||
GridLayoutManager glm = new GridLayoutManager(context, 3);
|
||||
|
@ -318,6 +326,7 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
// check if subscriptions exist
|
||||
if (suggestedChannelAdapter != null) {
|
||||
|
@ -338,6 +347,10 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
fetchSubscriptions();
|
||||
}
|
||||
}
|
||||
public void onPause() {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void loadFollowing() {
|
||||
// wrapper to just re-fetch subscriptions (upon user sign in, for example)
|
||||
|
@ -350,10 +363,13 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
}
|
||||
|
||||
private Map<String, Object> buildSuggestedOptions() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
|
||||
return Lbry.buildClaimSearchOptions(
|
||||
Claim.TYPE_CHANNEL,
|
||||
null,
|
||||
null,
|
||||
canShowMatureContent ? null : new ArrayList<>(Predefined.MATURE_TAGS),
|
||||
null,
|
||||
excludeChannelIdsForDiscover,
|
||||
Arrays.asList(Claim.ORDER_BY_EFFECTIVE_AMOUNT),
|
||||
|
@ -363,10 +379,13 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
}
|
||||
|
||||
private Map<String, Object> buildContentOptions() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
|
||||
return Lbry.buildClaimSearchOptions(
|
||||
Claim.TYPE_STREAM,
|
||||
null,
|
||||
null,
|
||||
canShowMatureContent ? null : new ArrayList<>(Predefined.MATURE_TAGS),
|
||||
getChannelIds(),
|
||||
null,
|
||||
getContentSortOrder(),
|
||||
|
@ -514,6 +533,7 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
}
|
||||
|
||||
contentClaimSearchLoading = true;
|
||||
Helper.setViewVisibility(noContentView, View.GONE);
|
||||
Map<String, Object> claimSearchOptions = buildContentOptions();
|
||||
contentClaimSearchTask = new ClaimSearchTask(claimSearchOptions, Lbry.LBRY_TV_CONNECTION_STRING, getLoadingView(), new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
@Override
|
||||
|
@ -551,11 +571,13 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
|
||||
contentHasReachedEnd = hasReachedEnd;
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception error) {
|
||||
contentClaimSearchLoading = false;
|
||||
checkNoContent(false);
|
||||
}
|
||||
});
|
||||
contentClaimSearchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
@ -571,10 +593,16 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
}
|
||||
|
||||
private void fetchSuggestedChannels() {
|
||||
if (suggestedClaimSearchLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
suggestedClaimSearchLoading = true;
|
||||
if (discoverDialog != null) {
|
||||
discoverDialog.setLoading(true);
|
||||
}
|
||||
|
||||
Helper.setViewVisibility(noContentView, View.GONE);
|
||||
suggestedChannelClaimSearchTask = new ClaimSearchTask(
|
||||
buildSuggestedOptions(),
|
||||
Lbry.LBRY_TV_CONNECTION_STRING,
|
||||
|
@ -600,6 +628,10 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
} else {
|
||||
suggestedChannelAdapter.addClaims(claims);
|
||||
}
|
||||
|
||||
if (discoverDialog == null || !discoverDialog.isVisible()) {
|
||||
checkNoContent(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -608,6 +640,9 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
if (discoverDialog != null) {
|
||||
discoverDialog.setLoading(false);
|
||||
}
|
||||
if (discoverDialog == null || !discoverDialog.isVisible()) {
|
||||
checkNoContent(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -645,14 +680,22 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
subscription.setUrl(claim.getPermanentUrl());
|
||||
String channelClaimId = claim.getClaimId();
|
||||
|
||||
ChannelSubscribeTask task = new ChannelSubscribeTask(getContext(), channelClaimId, subscription, false, null);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
updateSuggestedDoneButtonText();
|
||||
|
||||
ChannelSubscribeTask task = new ChannelSubscribeTask(getContext(), channelClaimId, subscription, false, new ChannelSubscribeTask.ChannelSubscribeHandler() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
if (discoverDialog != null) {
|
||||
fetchSubscriptions();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
|
||||
}
|
||||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
updateSuggestedDoneButtonText();
|
||||
}
|
||||
public void onChannelItemDeselected(Claim claim) {
|
||||
// unsubscribe
|
||||
Subscription subscription = new Subscription();
|
||||
|
@ -660,15 +703,35 @@ public class FollowingFragment extends BaseFragment implements FetchSubscription
|
|||
subscription.setUrl(claim.getPermanentUrl());
|
||||
String channelClaimId = claim.getClaimId();
|
||||
|
||||
ChannelSubscribeTask task = new ChannelSubscribeTask(getContext(), channelClaimId, subscription, true, null);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
updateSuggestedDoneButtonText();
|
||||
|
||||
ChannelSubscribeTask task = new ChannelSubscribeTask(getContext(), channelClaimId, subscription, true, new ChannelSubscribeTask.ChannelSubscribeHandler() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
if (discoverDialog != null) {
|
||||
fetchSubscriptions();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
|
||||
}
|
||||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
updateSuggestedDoneButtonText();
|
||||
}
|
||||
public void onChannelSelectionCleared() {
|
||||
|
||||
}
|
||||
|
||||
private void checkNoContent(boolean suggested) {
|
||||
RecyclerView.Adapter adpater = suggested ? suggestedChannelAdapter : contentListAdapter;
|
||||
boolean noContent = adpater == null || adpater.getItemCount() == 0;
|
||||
Helper.setViewVisibility(noContentView, noContent ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
||||
fetchClaimSearchContent(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.lbry.browser.ui.search;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -10,6 +11,7 @@ import android.widget.TextView;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
@ -29,7 +31,8 @@ import io.lbry.browser.utils.Lbry;
|
|||
import io.lbry.browser.utils.LbryUri;
|
||||
import lombok.Setter;
|
||||
|
||||
public class SearchFragment extends BaseFragment implements ClaimListAdapter.ClaimListItemListener {
|
||||
public class SearchFragment extends BaseFragment implements
|
||||
ClaimListAdapter.ClaimListItemListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private ClaimListAdapter resultListAdapter;
|
||||
private static final int PAGE_SIZE = 25;
|
||||
|
||||
|
@ -84,18 +87,20 @@ public class SearchFragment extends BaseFragment implements ClaimListAdapter.Cla
|
|||
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (resultListAdapter == null || resultListAdapter.getItemCount() == 0) {
|
||||
// new search
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
|
||||
if (!Helper.isNullOrEmpty(currentQuery)) {
|
||||
search(currentQuery, currentFrom);
|
||||
}
|
||||
}
|
||||
if (Helper.isNullOrEmpty(currentQuery)) {
|
||||
} else {
|
||||
noQueryView.setVisibility(View.VISIBLE);
|
||||
noResultsView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private boolean checkQuery(String query) {
|
||||
if (!Helper.isNullOrEmpty(query) && !query.equalsIgnoreCase(currentQuery)) {
|
||||
// new query, reset values
|
||||
|
@ -176,7 +181,10 @@ public class SearchFragment extends BaseFragment implements ClaimListAdapter.Cla
|
|||
}
|
||||
|
||||
searchLoading = true;
|
||||
LighthouseSearchTask task = new LighthouseSearchTask(currentQuery, PAGE_SIZE, currentFrom, false, null, loadingView, new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false);
|
||||
LighthouseSearchTask task = new LighthouseSearchTask(
|
||||
currentQuery, PAGE_SIZE, currentFrom, canShowMatureContent, null, loadingView, new ClaimSearchTask.ClaimSearchResultHandler() {
|
||||
@Override
|
||||
public void onSuccess(List<Claim> claims, boolean hasReachedEnd) {
|
||||
contentHasReachedEnd = hasReachedEnd;
|
||||
|
@ -227,4 +235,10 @@ public class SearchFragment extends BaseFragment implements ClaimListAdapter.Cla
|
|||
MainActivity.openFileClaim(claim, getContext());
|
||||
}
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
||||
search(currentQuery, currentFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,14 +32,15 @@ import java.util.Random;
|
|||
import io.lbry.browser.dialog.ContentFromDialogFragment;
|
||||
import io.lbry.browser.dialog.ContentSortDialogFragment;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import okhttp3.MediaType;
|
||||
|
||||
public final class Helper {
|
||||
public static final String METHOD_GET = "GET";
|
||||
public static final String METHOD_POST = "POST";
|
||||
public static final String ISO_DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
||||
public static final MediaType FORM_MEDIA_TYPE = MediaType.parse("application/x-www-form-urlencoded");
|
||||
public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=utf-8");
|
||||
public static final List<String> MATURE_TAG_NAMES = Arrays.asList("mature", "nsfw", "porn", "xxx");
|
||||
public static final int CONTENT_PAGE_SIZE = 25;
|
||||
|
||||
public static boolean isNull(String value) {
|
||||
|
@ -283,4 +284,43 @@ public final class Helper {
|
|||
((ColorDrawable) bg).setColor(isPlaceholder ? ContextCompat.getColor(context, android.R.color.transparent) : color);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Tag> getTagObjectsForTags(List<String> tags) {
|
||||
List<Tag> tagObjects = new ArrayList<>(tags.size());
|
||||
for (String tag : tags) {
|
||||
tagObjects.add(new Tag(tag));
|
||||
}
|
||||
return tagObjects;
|
||||
}
|
||||
public static List<String> getTagsForTagObjects(List<Tag> tagObjects) {
|
||||
List<String> tags = new ArrayList<>(tagObjects.size());
|
||||
for (Tag tagObject : tagObjects) {
|
||||
tags.add(tagObject.getLowercaseName());
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
public static List<Tag> mergeKnownTags(List<Tag> fetchedTags) {
|
||||
List<Tag> allKnownTags = getTagObjectsForTags(Predefined.DEFAULT_KNOWN_TAGS);
|
||||
List<Integer> followIndexes = new ArrayList<>();
|
||||
for (Tag tag : fetchedTags) {
|
||||
if (!allKnownTags.contains(tag)) {
|
||||
allKnownTags.add(tag);
|
||||
} else if (tag.isFollowed()) {
|
||||
followIndexes.add(allKnownTags.indexOf(tag));
|
||||
}
|
||||
}
|
||||
for (int index : followIndexes) {
|
||||
allKnownTags.get(index).setFollowed(true);
|
||||
}
|
||||
return allKnownTags;
|
||||
}
|
||||
public static List<Tag> filterFollowedTags(List<Tag> tags) {
|
||||
List<Tag> followedTags = new ArrayList<>();
|
||||
for (Tag tag : followedTags) {
|
||||
if (tag.isFollowed()) {
|
||||
followedTags.add(tag);
|
||||
}
|
||||
}
|
||||
return followedTags;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ import io.lbry.browser.exceptions.LbryRequestException;
|
|||
import io.lbry.browser.exceptions.LbryResponseException;
|
||||
import io.lbry.browser.model.Claim;
|
||||
import io.lbry.browser.model.ClaimCacheKey;
|
||||
import io.lbry.browser.model.ClaimSearchCacheValue;
|
||||
import io.lbry.browser.model.File;
|
||||
import io.lbry.browser.model.Tag;
|
||||
import io.lbry.browser.model.Transaction;
|
||||
import io.lbry.browser.model.WalletBalance;
|
||||
import io.lbry.browser.model.lbryinc.User;
|
||||
|
@ -39,13 +41,16 @@ import okhttp3.RequestBody;
|
|||
import okhttp3.Response;
|
||||
|
||||
public final class Lbry {
|
||||
public static final String TAG = "Lbry";
|
||||
public static LinkedHashMap<ClaimCacheKey, Claim> claimCache = new LinkedHashMap<>();
|
||||
public static LinkedHashMap<Map<String, Object>, List<Claim>> claimSearchCache = new LinkedHashMap<>();
|
||||
public static LinkedHashMap<Map<String, Object>, ClaimSearchCacheValue> claimSearchCache = new LinkedHashMap<>();
|
||||
public static WalletBalance walletBalance = new WalletBalance();
|
||||
public static List<Tag> knownTags = new ArrayList<>();
|
||||
public static List<Tag> followedTags = new ArrayList<>();
|
||||
|
||||
public static final int TTL_CLAIM_SEARCH_VALUE = 120000; // 2-minute TTL for cache
|
||||
public static final String SDK_CONNECTION_STRING = "http://127.0.0.1:5279";
|
||||
public static final String LBRY_TV_CONNECTION_STRING = "https://api.lbry.tv/api/v1/proxy";
|
||||
public static final String TAG = "Lbry";
|
||||
|
||||
// Values to obtain from LBRY SDK status
|
||||
public static boolean IS_STATUS_PARSED = false; // Check if the status has been parsed at least once
|
||||
|
@ -360,7 +365,10 @@ public final class Lbry {
|
|||
|
||||
public static List<Claim> claimSearch(Map<String, Object> options, String connectionString) throws ApiCallException {
|
||||
if (claimSearchCache.containsKey(options)) {
|
||||
return claimSearchCache.get(options);
|
||||
ClaimSearchCacheValue value = claimSearchCache.get(options);
|
||||
if (!value.isExpired(TTL_CLAIM_SEARCH_VALUE)) {
|
||||
return claimSearchCache.get(options).getClaims();
|
||||
}
|
||||
}
|
||||
|
||||
List<Claim> claims = new ArrayList<>();
|
||||
|
@ -377,7 +385,7 @@ public final class Lbry {
|
|||
}
|
||||
}
|
||||
|
||||
claimSearchCache.put(options, claims);
|
||||
claimSearchCache.put(options, new ClaimSearchCacheValue(claims, System.currentTimeMillis()));
|
||||
} catch (LbryRequestException | LbryResponseException | JSONException ex) {
|
||||
throw new ApiCallException("Could not execute resolve call", ex);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,10 @@ public class LbryUri {
|
|||
}
|
||||
}
|
||||
|
||||
if (components.size() == 0) {
|
||||
throw new LbryUriException("Regular expression error occurred while trying to parse the value");
|
||||
}
|
||||
|
||||
// components[0] = proto
|
||||
// components[1] = streamNameOrChannelName
|
||||
// components[2] = primaryModSeparator
|
||||
|
@ -141,7 +145,7 @@ public class LbryUri {
|
|||
return uri;
|
||||
}
|
||||
|
||||
public String build(boolean includeProto, String protoDefault) {
|
||||
public String build(boolean includeProto, String protoDefault, boolean vanity) {
|
||||
String formattedChannelName = null;
|
||||
if (channelName != null) {
|
||||
formattedChannelName = channelName.startsWith("@") ? channelName : String.format("@%s", channelName);
|
||||
|
@ -162,6 +166,15 @@ public class LbryUri {
|
|||
primaryClaimId = !Helper.isNullOrEmpty(formattedChannelName) ? channelClaimId : streamClaimId;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (includeProto) {
|
||||
sb.append(protoDefault);
|
||||
}
|
||||
sb.append(primaryClaimName);
|
||||
if (vanity) {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String secondaryClaimName = null;
|
||||
if (Helper.isNullOrEmpty(claimName) && !Helper.isNullOrEmpty(contentName)) {
|
||||
secondaryClaimName = contentName;
|
||||
|
@ -171,11 +184,6 @@ public class LbryUri {
|
|||
}
|
||||
String secondaryClaimId = !Helper.isNullOrEmpty(secondaryClaimName) ? streamClaimId : null;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (includeProto) {
|
||||
sb.append(protoDefault);
|
||||
}
|
||||
sb.append(primaryClaimName);
|
||||
if (!Helper.isNullOrEmpty(primaryClaimId)) {
|
||||
sb.append('#').append(primaryClaimId);
|
||||
}
|
||||
|
@ -205,8 +213,11 @@ public class LbryUri {
|
|||
return parse(url).toString();
|
||||
}
|
||||
|
||||
public String toVanityString() {
|
||||
return build(true, PROTO_DEFAULT, true);
|
||||
}
|
||||
public String toString() {
|
||||
return build(true, PROTO_DEFAULT);
|
||||
return build(true, PROTO_DEFAULT, false);
|
||||
}
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
|
|
492
app/src/main/java/io/lbry/browser/utils/Predefined.java
Normal file
492
app/src/main/java/io/lbry/browser/utils/Predefined.java
Normal file
|
@ -0,0 +1,492 @@
|
|||
package io.lbry.browser.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class Predefined {
|
||||
public static final List<String> DEFAULT_KNOWN_TAGS = Arrays.asList(
|
||||
"free speech",
|
||||
"censorship",
|
||||
"gaming",
|
||||
"pop culture",
|
||||
"entertainment",
|
||||
"technology",
|
||||
"music",
|
||||
"funny",
|
||||
"education",
|
||||
"learning",
|
||||
"news",
|
||||
"gameplay",
|
||||
"nature",
|
||||
"beliefs",
|
||||
"comedy",
|
||||
"games",
|
||||
"film & animation",
|
||||
"whothinks",
|
||||
"game",
|
||||
"weapons",
|
||||
"blockchain",
|
||||
"video game",
|
||||
"sports",
|
||||
"walkthrough",
|
||||
"art",
|
||||
"pc",
|
||||
"minecraft",
|
||||
"playthrough",
|
||||
"economics",
|
||||
"automotive",
|
||||
"play",
|
||||
"tutorial",
|
||||
"twitch",
|
||||
"how to",
|
||||
"ps4",
|
||||
"bitcoin",
|
||||
"fortnite",
|
||||
"commentary",
|
||||
"lets play",
|
||||
"fun",
|
||||
"politics",
|
||||
"travel",
|
||||
"food",
|
||||
"science",
|
||||
"xbox",
|
||||
"liberal",
|
||||
"democrat",
|
||||
"progressive",
|
||||
"survival",
|
||||
"non-profits",
|
||||
"activism",
|
||||
"cryptocurrency",
|
||||
"playstation",
|
||||
"nintendo",
|
||||
"government",
|
||||
"steam",
|
||||
"podcast",
|
||||
"gamer",
|
||||
"horror",
|
||||
"conservative",
|
||||
"reaction",
|
||||
"trailer",
|
||||
"love",
|
||||
"cnn",
|
||||
"republican",
|
||||
"political",
|
||||
"hangoutsonair",
|
||||
"hoa",
|
||||
"msnbc",
|
||||
"cbs",
|
||||
"anime",
|
||||
"donald trump",
|
||||
"fiction",
|
||||
"fox news",
|
||||
"crypto",
|
||||
"ethereum",
|
||||
"call of duty",
|
||||
"android",
|
||||
"multiplayer",
|
||||
"epic",
|
||||
"rpg",
|
||||
"adventure",
|
||||
"secular talk",
|
||||
"btc",
|
||||
"atheist",
|
||||
"atheism",
|
||||
"video games",
|
||||
"ps3",
|
||||
"cod",
|
||||
"online",
|
||||
"agnostic",
|
||||
"movie",
|
||||
"fps",
|
||||
"lets",
|
||||
"mod",
|
||||
"world",
|
||||
"reviews",
|
||||
"sharefactory",
|
||||
"space",
|
||||
"pokemon",
|
||||
"stream",
|
||||
"hilarious",
|
||||
"lol",
|
||||
"sony",
|
||||
"god",
|
||||
"dance",
|
||||
"pvp",
|
||||
"tech",
|
||||
"strategy",
|
||||
"zombies",
|
||||
"fail",
|
||||
"film",
|
||||
"xbox360",
|
||||
"animation",
|
||||
"unboxing",
|
||||
"money",
|
||||
"wwe",
|
||||
"mods",
|
||||
"indie",
|
||||
"pubg",
|
||||
"ios",
|
||||
"history",
|
||||
"rap",
|
||||
"mobile",
|
||||
"trump",
|
||||
"hack",
|
||||
"flat earth",
|
||||
"trap",
|
||||
"humor",
|
||||
"vlogging",
|
||||
"fox",
|
||||
"news radio",
|
||||
"facebook",
|
||||
"edm",
|
||||
"fitness",
|
||||
"vaping",
|
||||
"hip hop",
|
||||
"secular",
|
||||
"jesus",
|
||||
"song",
|
||||
"vape",
|
||||
"guitar",
|
||||
"remix",
|
||||
"mining",
|
||||
"daily",
|
||||
"diy",
|
||||
"pets",
|
||||
"videogame",
|
||||
"death",
|
||||
"funny moments",
|
||||
"religion",
|
||||
"media",
|
||||
"viral",
|
||||
"war",
|
||||
"nbc",
|
||||
"freedom",
|
||||
"gold",
|
||||
"family",
|
||||
"meme",
|
||||
"zombie",
|
||||
"photography",
|
||||
"chill",
|
||||
"sniper",
|
||||
"computer",
|
||||
"iphone",
|
||||
"dragon",
|
||||
"bible",
|
||||
"pro",
|
||||
"overwatch",
|
||||
"litecoin",
|
||||
"gta",
|
||||
"house",
|
||||
"fire",
|
||||
"bass",
|
||||
"truth",
|
||||
"crash",
|
||||
"mario",
|
||||
"league of legends",
|
||||
"wii",
|
||||
"mmorpg",
|
||||
"health",
|
||||
"marvel",
|
||||
"racing",
|
||||
"apple",
|
||||
"instrumental",
|
||||
"earth",
|
||||
"destiny",
|
||||
"satire",
|
||||
"race",
|
||||
"training",
|
||||
"electronic",
|
||||
"boss",
|
||||
"roblox",
|
||||
"family friendly",
|
||||
"california",
|
||||
"react",
|
||||
"christian",
|
||||
"mmo",
|
||||
"twitter",
|
||||
"help",
|
||||
"star",
|
||||
"cars",
|
||||
"random",
|
||||
"top 10",
|
||||
"ninja",
|
||||
"guns",
|
||||
"linux",
|
||||
"lessons",
|
||||
"vegan",
|
||||
"future",
|
||||
"dota 2",
|
||||
"studio",
|
||||
"star wars",
|
||||
"shooting",
|
||||
"nasa",
|
||||
"rock",
|
||||
"league",
|
||||
"subscribe",
|
||||
"water",
|
||||
"gta v",
|
||||
"car",
|
||||
"samsung",
|
||||
"music video",
|
||||
"skyrim",
|
||||
"dog",
|
||||
"comics",
|
||||
"shooter game",
|
||||
"bo3",
|
||||
"halloween",
|
||||
"liberty",
|
||||
"eth",
|
||||
"conspiracy",
|
||||
"knife",
|
||||
"fashion",
|
||||
"stories",
|
||||
"vapor",
|
||||
"nvidia",
|
||||
"cute",
|
||||
"beat",
|
||||
"nintendo switch",
|
||||
"fantasy",
|
||||
"christmas",
|
||||
"world of warcraft",
|
||||
"industry",
|
||||
"cartoon",
|
||||
"garden",
|
||||
"animals",
|
||||
"windows",
|
||||
"happy",
|
||||
"magic",
|
||||
"memes",
|
||||
"design",
|
||||
"tactical",
|
||||
"fallout 4",
|
||||
"puzzle",
|
||||
"parody",
|
||||
"rv",
|
||||
"beats",
|
||||
"building",
|
||||
"disney",
|
||||
"drone",
|
||||
"ps2",
|
||||
"beach",
|
||||
"metal",
|
||||
"christianity",
|
||||
"business",
|
||||
"mix",
|
||||
"bo2",
|
||||
"cover",
|
||||
"senate",
|
||||
"4k",
|
||||
"united states",
|
||||
"final",
|
||||
"hero",
|
||||
"playing",
|
||||
"dlc",
|
||||
"ubisoft",
|
||||
"halo",
|
||||
"pc gaming",
|
||||
"raw",
|
||||
"investing",
|
||||
"online learning",
|
||||
"software",
|
||||
"ark",
|
||||
"mojang",
|
||||
"console",
|
||||
"battle royale",
|
||||
"canon",
|
||||
"microsoft",
|
||||
"camping",
|
||||
"ufo",
|
||||
"progressive talk",
|
||||
"switch",
|
||||
"fpv",
|
||||
"arcade",
|
||||
"school",
|
||||
"driving",
|
||||
"bodybuilding",
|
||||
"drama",
|
||||
"retro",
|
||||
"science fiction",
|
||||
"eggs",
|
||||
"australia",
|
||||
"modded",
|
||||
"rainbow",
|
||||
"gamers",
|
||||
"resident evil",
|
||||
"drawing",
|
||||
"brasil",
|
||||
"england",
|
||||
"hillary clinton",
|
||||
"singing",
|
||||
"final fantasy",
|
||||
"hiphop",
|
||||
"video blog",
|
||||
"mature",
|
||||
"quad",
|
||||
"noob",
|
||||
"simulation",
|
||||
"illuminati",
|
||||
"poetry",
|
||||
"dayz",
|
||||
"manga",
|
||||
"howto",
|
||||
"insane",
|
||||
"press",
|
||||
"special",
|
||||
"church",
|
||||
"ico",
|
||||
"weird",
|
||||
"libertarian",
|
||||
"crafting",
|
||||
"level",
|
||||
"comic",
|
||||
"sandbox",
|
||||
"daily vlog",
|
||||
"outdoor",
|
||||
"black ops",
|
||||
"sound",
|
||||
"christ",
|
||||
"duty",
|
||||
"juvenile fiction",
|
||||
"pc game",
|
||||
"how-to",
|
||||
"ww2",
|
||||
"creepy",
|
||||
"artist",
|
||||
"galaxy",
|
||||
"destiny 2",
|
||||
"new music",
|
||||
"quest",
|
||||
"lee",
|
||||
"pacman",
|
||||
"super smash bros",
|
||||
"day",
|
||||
"survival horror",
|
||||
"patreon",
|
||||
"bitcoin price",
|
||||
"trending",
|
||||
"open world",
|
||||
"wii u",
|
||||
"dope",
|
||||
"reaper",
|
||||
"sniping",
|
||||
"dubstep",
|
||||
"truck",
|
||||
"planet",
|
||||
"dc",
|
||||
"amazon",
|
||||
"spirituality",
|
||||
"universe",
|
||||
"video game culture",
|
||||
"community",
|
||||
"cat",
|
||||
"aliens",
|
||||
"tourism",
|
||||
"altcoins",
|
||||
"style",
|
||||
"travel trailer",
|
||||
"rda",
|
||||
"gun",
|
||||
"secret",
|
||||
"far cry 5",
|
||||
"auto",
|
||||
"culture",
|
||||
"dj",
|
||||
"mw2",
|
||||
"lord",
|
||||
"full time rving",
|
||||
"role-playing game",
|
||||
"prank",
|
||||
"grand theft auto",
|
||||
"master",
|
||||
"wrestling",
|
||||
"sci-fi",
|
||||
"workout",
|
||||
"ghost",
|
||||
"fake news",
|
||||
"silly",
|
||||
"season",
|
||||
"bo4",
|
||||
"trading",
|
||||
"extreme",
|
||||
"economy",
|
||||
"combat",
|
||||
"plays",
|
||||
"muslim",
|
||||
"pubg mobile",
|
||||
"clips",
|
||||
"bo1",
|
||||
"paypal",
|
||||
"sims",
|
||||
"exploration",
|
||||
"light",
|
||||
"ripple",
|
||||
"paranormal",
|
||||
"football",
|
||||
"capcom",
|
||||
"rta",
|
||||
"discord",
|
||||
"batman",
|
||||
"player",
|
||||
"server",
|
||||
"anarchy",
|
||||
"military",
|
||||
"playlist",
|
||||
"cosplay",
|
||||
"rv park",
|
||||
"rant",
|
||||
"edit",
|
||||
"germany",
|
||||
"reading",
|
||||
"chris",
|
||||
"flash",
|
||||
"loot",
|
||||
"bitcoin gratis",
|
||||
"game reviews",
|
||||
"movies",
|
||||
"stupid",
|
||||
"latest news",
|
||||
"squad gameplay",
|
||||
"guru",
|
||||
"timelapse",
|
||||
"black ops 3",
|
||||
"holiday",
|
||||
"soul",
|
||||
"motivation",
|
||||
"mw3",
|
||||
"vacation",
|
||||
"sega",
|
||||
"19th century",
|
||||
"pop",
|
||||
"sims 4",
|
||||
"post",
|
||||
"smok",
|
||||
"island",
|
||||
"scotland",
|
||||
"paladins",
|
||||
"warrior",
|
||||
"creepypasta",
|
||||
"role-playing",
|
||||
"solar",
|
||||
"vr",
|
||||
"animal",
|
||||
"peace",
|
||||
"consciousness",
|
||||
"dota",
|
||||
"audio",
|
||||
"mass effect",
|
||||
"humour",
|
||||
"first look",
|
||||
"videogames",
|
||||
"future bass",
|
||||
"freestyle",
|
||||
"hardcore",
|
||||
"portugal",
|
||||
"dantdm",
|
||||
"teaser",
|
||||
"lbry",
|
||||
"coronavirus",
|
||||
"covidcuts",
|
||||
"covid-19"
|
||||
);
|
||||
public static final List<String> MATURE_TAGS = Arrays.asList("mature", "nsfw", "porn", "xxx");
|
||||
}
|
11
app/src/main/res/drawable-anydpi/ic_add.xml
Normal file
11
app/src/main/res/drawable-anydpi/ic_add.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF"
|
||||
android:alpha="0.8">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
BIN
app/src/main/res/drawable-hdpi/ic_add.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 B |
BIN
app/src/main/res/drawable-mdpi/ic_add.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 B |
BIN
app/src/main/res/drawable-xhdpi/ic_add.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 127 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_add.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_add.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 164 B |
|
@ -148,7 +148,8 @@
|
|||
android:layout_marginRight="24dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:textSize="12sp"
|
||||
android:textFontWeight="300" />
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
<TextView
|
||||
android:id="@+id/file_view_publish_time"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
android:fontFamily="@font/inter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="36dp"
|
||||
android:hint="@string/uri_placeholder"
|
||||
android:singleLine="true"
|
||||
android:textFontWeight="300"
|
||||
|
|
94
app/src/main/res/layout/dialog_content_scope.xml
Normal file
94
app/src/main/res/layout/dialog_content_scope.xml
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/filter_for"
|
||||
android:textSize="12sp"
|
||||
android:textAllCaps="true"
|
||||
android:textStyle="bold" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="@color/divider"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/content_scope_everyone_item"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
<io.lbry.browser.ui.controls.SolidIconView
|
||||
android:id="@+id/content_scope_everyone_item_icon"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:textSize="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/fa_globe_americas" />
|
||||
<TextView
|
||||
android:layout_toRightOf="@+id/content_scope_everyone_item_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/everyone"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300" />
|
||||
<ImageView
|
||||
android:id="@+id/content_scope_everyone_item_selected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_check"
|
||||
android:tint="@color/lbryGreen"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/content_scope_tags_item"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
<io.lbry.browser.ui.controls.SolidIconView
|
||||
android:id="@+id/content_scope_tags_item_icon"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:textSize="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="@string/fa_hashtag" />
|
||||
<TextView
|
||||
android:layout_toRightOf="@+id/content_scope_tags_item_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/tags_you_follow"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300" />
|
||||
<ImageView
|
||||
android:id="@+id/content_scope_tags_item_selected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_check"
|
||||
android:tint="@color/lbryGreen"
|
||||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
106
app/src/main/res/layout/dialog_customize_tags.xml
Normal file
106
app/src/main/res/layout/dialog_customize_tags.xml
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/customize_your_tags"
|
||||
android:textSize="12sp"
|
||||
android:textAllCaps="true"
|
||||
android:textStyle="bold" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="@color/divider"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/customize_tags_followed_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp" />
|
||||
<TextView
|
||||
android:id="@+id/customize_no_followed_tags"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/no_followed_tags"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="@color/divider"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/customize_tags_suggested_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/customize_no_tag_results"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/no_tag_results"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/customize_tag_filter_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/search_for_more_tags"
|
||||
android:fontFamily="@font/inter"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/customize_done_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/done" />
|
||||
</LinearLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -180,6 +180,17 @@
|
|||
android:visibility="gone" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/all_content_no_claim_search_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/no_claim_search_content"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/all_content_list"
|
||||
android:clipToPadding="false"
|
||||
|
|
|
@ -98,6 +98,17 @@
|
|||
android:layout_alignParentRight="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/channel_content_no_claim_search_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/no_claim_search_content"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/channel_content_list"
|
||||
android:clipToPadding="false"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -159,6 +158,17 @@
|
|||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/following_no_claim_search_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:fontFamily="@font/inter"
|
||||
android:text="@string/no_claim_search_content"
|
||||
android:textSize="14sp"
|
||||
android:textFontWeight="300"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/following_suggested_grid"
|
||||
android:clipToPadding="false"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:fontFamily="@font/inter"
|
||||
android:textAllCaps="true"
|
||||
android:textSize="48sp"
|
||||
android:textColor="@color/white" />
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:clickable="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="12dp"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
<ImageView
|
||||
android:id="@+id/tag_action"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:tint="@color/darkForeground" />
|
||||
<TextView
|
||||
android:id="@+id/tag_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:textSize="12sp"
|
||||
android:textFontWeight="300" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:showIn="navigation_view">
|
||||
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_home"
|
||||
android:icon="@drawable/ic_menu_camera"
|
||||
android:title="@string/menu_home" />
|
||||
<item
|
||||
android:id="@+id/nav_gallery"
|
||||
android:icon="@drawable/ic_menu_gallery"
|
||||
android:title="@string/menu_gallery" />
|
||||
<item
|
||||
android:id="@+id/nav_slideshow"
|
||||
android:icon="@drawable/ic_menu_slideshow"
|
||||
android:title="@string/menu_slideshow" />
|
||||
</group>
|
||||
</menu>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
|
@ -13,6 +13,7 @@
|
|||
<color name="selectedNavItem">#0E0E0E</color>
|
||||
<color name="channelCoverBackground">#CC000000</color>
|
||||
|
||||
<color name="darkForeground">#EEEEEE</color>
|
||||
<color name="foreground">#999999</color>
|
||||
<color name="mediaContainerBackground">#333333</color>
|
||||
<color name="borderTextArea">#5F5F5F</color>
|
||||
|
@ -21,8 +22,8 @@
|
|||
<color name="brighterLbryGreen">#40B887</color>
|
||||
<color name="lbryGreen">#2F9176</color>
|
||||
<color name="nextLbryGreen">#38D9A9</color>
|
||||
<color name="tagGreen">#E3F6F1</color>
|
||||
<color name="tagGrape">#77F255DA</color>
|
||||
<color name="tagGreen">#329A7E</color>
|
||||
<color name="tagGrape">#77D510B8</color>
|
||||
|
||||
<color name="divider">#454545</color>
|
||||
<color name="lightDivider">#0A0A0A</color>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
<color name="selectedNavItem">#F1F1F1</color>
|
||||
<color name="channelCoverBackground">#CC000000</color>
|
||||
|
||||
<color name="darkForeground">#222222</color>
|
||||
<color name="foreground">#333333</color>
|
||||
<color name="mediaContainerBackground">#333333</color>
|
||||
<color name="borderTextArea">#777777</color>
|
||||
|
|
|
@ -2,21 +2,11 @@
|
|||
<string name="app_name">LBRY</string>
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="nav_header_desc">Navigation header</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
|
||||
<string name="menu_home">Home</string>
|
||||
<string name="menu_gallery">Gallery</string>
|
||||
<string name="menu_slideshow">Slideshow</string>
|
||||
|
||||
<string name="home_second">Home Second</string>
|
||||
|
||||
|
||||
<!-- Nav menu -->
|
||||
<string name="find_content">Find Content</string>
|
||||
<string name="your_content">Your Content</string>
|
||||
<string name="wallet">Wallet</string>
|
||||
|
||||
<string name="following">Following</string>
|
||||
<string name="editors_choice">Editor\'s Choice</string>
|
||||
<string name="your_tags">Your Tags</string>
|
||||
|
@ -29,9 +19,9 @@
|
|||
<string name="invites">Invites</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="about">About</string>
|
||||
|
||||
<string name="sign_in">Sign In</string>
|
||||
<string name="startup_failed">App startup failed. Please check your data connection and try again. If this problem persists, please email hello@lbry.com</string>
|
||||
<string name="no_claim_search_content">No content to display at this time. Please refine your selection or check back later.</string>
|
||||
|
||||
<!-- Welcome Page -->
|
||||
<string name="welcome_to_lbry">Welcome to LBRY.</string>
|
||||
|
@ -59,7 +49,6 @@
|
|||
|
||||
<!-- File view -->
|
||||
<string name="tags">Tags</string>
|
||||
|
||||
<string name="share">Share</string>
|
||||
<string name="repost">Repost</string>
|
||||
<string name="tip">Tip</string>
|
||||
|
@ -79,10 +68,11 @@
|
|||
<string name="website">Website</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="user_interface">User interface</string>
|
||||
<string name="user_interface">Content & User interface</string>
|
||||
<string name="other">Other</string>
|
||||
<string name="enable_dark_mode">Enable dark theme</string>
|
||||
<string name="show_url_suggestions">Show URL suggestsions</string>
|
||||
<string name="show_mature_content">Show mature content</string>
|
||||
<string name="show_url_suggestions">Show URL suggestions</string>
|
||||
<string name="notifications">Notifications</string>
|
||||
<string name="subscriptions">Subscriptions</string>
|
||||
<string name="content_interests">Content Interests</string>
|
||||
|
@ -172,6 +162,7 @@
|
|||
</plurals>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<string name="customize_your_tags">Customize your tags</string>
|
||||
<string name="sort_content_by">Sort content by</string>
|
||||
<string name="content_from">Content from</string>
|
||||
<string name="trending_content">Trending content</string>
|
||||
|
@ -187,9 +178,16 @@
|
|||
<string name="all_time">All time</string>
|
||||
<string name="from">from</string>
|
||||
<string name="for_text">for</string>
|
||||
<string name="filter_for">Filter for</string>
|
||||
<string name="everyone">Everyone</string>
|
||||
<string name="tags_you_follow">Tags you follow</string>
|
||||
<string name="customize">Customize</string>
|
||||
<string name="not_yet_implemented">The selected view is not yet available.</string>
|
||||
<string name="customize_tags_hint">It looks like you have not followed any tags yet.</string>
|
||||
<string name="search_for_more_tags">Search for more tags</string>
|
||||
<string name="no_followed_tags">You have not followed any tags yet. Get started by adding tags that you are interested in!</string>
|
||||
<string name="no_tag_results">We could not find new tags that you\'re not following.</string>
|
||||
<string name="tag_already_followed">The \'%1$s\' tag has already been added.</string>
|
||||
|
||||
<!-- Verification -->
|
||||
<string name="provide_email_address">Please provide an email address.</string>
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
app:key="io.lbry.browser.preference.userinterface.DarkMode"
|
||||
app:title="@string/enable_dark_mode"
|
||||
app:iconSpaceReserved="false" />
|
||||
<SwitchPreferenceCompat
|
||||
app:key="io.lbry.browser.preference.userinterface.ShowMatureContent"
|
||||
app:title="@string/show_mature_content"
|
||||
app:iconSpaceReserved="false" />
|
||||
<SwitchPreferenceCompat
|
||||
app:key="io.lbry.browser.preference.userinterface.UrlSuggestions"
|
||||
app:title="@string/show_url_suggestions"
|
||||
|
@ -23,14 +27,17 @@
|
|||
<SwitchPreferenceCompat
|
||||
app:key="io.lbry.browser.preference.notifications.Subscriptions"
|
||||
app:title="@string/subscriptions"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
<SwitchPreferenceCompat
|
||||
app:key="io.lbry.browser.preference.notifications.Rewards"
|
||||
app:title="@string/rewards"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
<SwitchPreferenceCompat
|
||||
app:key="io.lbry.browser.preference.notifications.ContentInterests"
|
||||
app:title="@string/content_interests"
|
||||
app:defaultValue="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
Loading…
Reference in a new issue