Native rewrite #878
24 changed files with 854 additions and 40 deletions
|
@ -21,6 +21,7 @@ import android.os.Handler;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
@ -74,6 +75,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import io.lbry.browser.adapter.NavigationMenuAdapter;
|
import io.lbry.browser.adapter.NavigationMenuAdapter;
|
||||||
import io.lbry.browser.adapter.UrlSuggestionListAdapter;
|
import io.lbry.browser.adapter.UrlSuggestionListAdapter;
|
||||||
import io.lbry.browser.data.DatabaseHelper;
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.dialog.ContentScopeDialogFragment;
|
||||||
import io.lbry.browser.exceptions.LbryUriException;
|
import io.lbry.browser.exceptions.LbryUriException;
|
||||||
import io.lbry.browser.listener.SdkStatusListener;
|
import io.lbry.browser.listener.SdkStatusListener;
|
||||||
import io.lbry.browser.listener.WalletBalanceListener;
|
import io.lbry.browser.listener.WalletBalanceListener;
|
||||||
|
@ -86,9 +88,14 @@ import io.lbry.browser.model.WalletBalance;
|
||||||
import io.lbry.browser.model.WalletSync;
|
import io.lbry.browser.model.WalletSync;
|
||||||
import io.lbry.browser.model.lbryinc.Subscription;
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
||||||
|
import io.lbry.browser.tasks.MergeSubscriptionsTask;
|
||||||
import io.lbry.browser.tasks.ResolveTask;
|
import io.lbry.browser.tasks.ResolveTask;
|
||||||
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
|
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
|
||||||
|
import io.lbry.browser.tasks.wallet.LoadSharedUserStateTask;
|
||||||
|
import io.lbry.browser.tasks.wallet.SaveSharedUserStateTask;
|
||||||
|
import io.lbry.browser.tasks.wallet.SyncApplyTask;
|
||||||
import io.lbry.browser.tasks.wallet.SyncGetTask;
|
import io.lbry.browser.tasks.wallet.SyncGetTask;
|
||||||
|
import io.lbry.browser.tasks.wallet.SyncSetTask;
|
||||||
import io.lbry.browser.tasks.wallet.WalletBalanceTask;
|
import io.lbry.browser.tasks.wallet.WalletBalanceTask;
|
||||||
import io.lbry.browser.ui.BaseFragment;
|
import io.lbry.browser.ui.BaseFragment;
|
||||||
import io.lbry.browser.ui.channel.ChannelFragment;
|
import io.lbry.browser.ui.channel.ChannelFragment;
|
||||||
|
@ -104,7 +111,6 @@ import io.lbry.browser.utils.Lbryio;
|
||||||
import io.lbry.lbrysdk.LbrynetService;
|
import io.lbry.lbrysdk.LbrynetService;
|
||||||
import io.lbry.lbrysdk.ServiceHelper;
|
import io.lbry.lbrysdk.ServiceHelper;
|
||||||
import io.lbry.lbrysdk.Utils;
|
import io.lbry.lbrysdk.Utils;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements SdkStatusListener {
|
public class MainActivity extends AppCompatActivity implements SdkStatusListener {
|
||||||
|
@ -190,6 +196,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
@Getter
|
@Getter
|
||||||
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
private boolean walletBalanceUpdateScheduled;
|
private boolean walletBalanceUpdateScheduled;
|
||||||
|
private boolean walletSyncScheduled;
|
||||||
private String pendingAllContentTag;
|
private String pendingAllContentTag;
|
||||||
private String pendingChannelUrl;
|
private String pendingChannelUrl;
|
||||||
private boolean pendingFollowingReload;
|
private boolean pendingFollowingReload;
|
||||||
|
@ -836,6 +843,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
}, CHECK_SDK_READY_INTERVAL);
|
}, CHECK_SDK_READY_INTERVAL);
|
||||||
} else {
|
} else {
|
||||||
scheduleWalletBalanceUpdate();
|
scheduleWalletBalanceUpdate();
|
||||||
|
scheduleWalletSyncTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,7 +851,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
if (Lbryio.isSignedIn()) {
|
if (Lbryio.isSignedIn()) {
|
||||||
checkSyncedWallet();
|
checkSyncedWallet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//overrideRemoteWallet();
|
||||||
scheduleWalletBalanceUpdate();
|
scheduleWalletBalanceUpdate();
|
||||||
|
scheduleWalletSyncTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleWalletBalanceUpdate() {
|
private void scheduleWalletBalanceUpdate() {
|
||||||
|
@ -858,6 +869,157 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scheduleWalletSyncTask() {
|
||||||
|
if (scheduler != null && !walletSyncScheduled) {
|
||||||
|
scheduler.scheduleAtFixedRate(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
syncWalletAndLoadPreferences();
|
||||||
|
}
|
||||||
|
}, 0, 5, TimeUnit.MINUTES);
|
||||||
|
walletSyncScheduled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSharedUserState() {
|
||||||
|
if (!userSyncEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveSharedUserStateTask saveTask = new SaveSharedUserStateTask(new SaveSharedUserStateTask.SaveSharedUserStateHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
// push wallet sync changes
|
||||||
|
pushCurrentWalletSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
saveTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSharedUserState() {
|
||||||
|
// load wallet preferences
|
||||||
|
LoadSharedUserStateTask loadTask = new LoadSharedUserStateTask(MainActivity.this, new LoadSharedUserStateTask.LoadSharedUserStateHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Subscription> subscriptions, List<Tag> followedTags) {
|
||||||
|
if (subscriptions != null && subscriptions.size() > 0) {
|
||||||
|
// reload subscriptions if wallet fragment is FollowingFragment
|
||||||
|
//openNavFragments.get
|
||||||
|
MergeSubscriptionsTask mergeTask = new MergeSubscriptionsTask(
|
||||||
|
subscriptions, MainActivity.this, new MergeSubscriptionsTask.MergeSubscriptionsHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Subscription> subscriptions, List<Subscription> diff) {
|
||||||
|
Lbryio.subscriptions = new ArrayList<>(subscriptions);
|
||||||
|
if (diff != null && diff.size() > 0) {
|
||||||
|
saveSharedUserState();
|
||||||
|
}
|
||||||
|
for (Fragment fragment : openNavFragments.values()) {
|
||||||
|
if (fragment instanceof FollowingFragment) {
|
||||||
|
// reload local subscriptions
|
||||||
|
((FollowingFragment) fragment).fetchLoadedSubscriptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
Log.e(TAG, String.format("merge subscriptions failed: %s", error.getMessage()), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mergeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (followedTags != null && followedTags.size() > 0) {
|
||||||
|
List<Tag> previousTags = new ArrayList<>(Lbry.followedTags);
|
||||||
|
Lbry.followedTags = new ArrayList<>(followedTags);
|
||||||
|
for (Fragment fragment : openNavFragments.values()) {
|
||||||
|
if (fragment instanceof AllContentFragment) {
|
||||||
|
AllContentFragment acFragment = (AllContentFragment) fragment;
|
||||||
|
if (!acFragment.isSingleTagView() &&
|
||||||
|
acFragment.getCurrentContentScope() == ContentScopeDialogFragment.ITEM_TAGS &&
|
||||||
|
!previousTags.equals(followedTags)) {
|
||||||
|
acFragment.fetchClaimSearchContent(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
Log.e(TAG, String.format("load shared user state failed: %s", error != null ? error.getMessage() : "no error message"), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushCurrentWalletSync() {
|
||||||
|
String password = Utils.getSecureValue(SECURE_VALUE_KEY_SAVED_PASSWORD, this, Lbry.KEYSTORE);
|
||||||
|
SyncApplyTask fetchTask = new SyncApplyTask(true, password, new DefaultSyncTaskHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSyncApplySuccess(String hash, String data) {
|
||||||
|
SyncSetTask setTask = new SyncSetTask(Lbryio.lastRemoteHash, hash, data, null);
|
||||||
|
setTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onSyncApplyError(Exception error) { }
|
||||||
|
});
|
||||||
|
fetchTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean userSyncEnabled() {
|
||||||
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
boolean walletSyncEnabled = sp.getBoolean(PREFERENCE_KEY_INTERNAL_WALLET_SYNC_ENABLED, false);
|
||||||
|
return walletSyncEnabled && Lbryio.isSignedIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncWalletAndLoadPreferences() {
|
||||||
|
if (!userSyncEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String password = Utils.getSecureValue(SECURE_VALUE_KEY_SAVED_PASSWORD, this, Lbry.KEYSTORE);
|
||||||
|
SyncGetTask task = new SyncGetTask(password, true, null, new DefaultSyncTaskHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSyncGetSuccess(WalletSync walletSync) {
|
||||||
|
Lbryio.lastWalletSync = walletSync;
|
||||||
|
Lbryio.lastRemoteHash = walletSync.getHash();
|
||||||
|
loadSharedUserState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSyncGetWalletNotFound() {
|
||||||
|
// pass. This actually shouldn't happen at this point.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSyncGetError(Exception error) {
|
||||||
|
// pass
|
||||||
|
Log.e(TAG, String.format("sync get failed: %s", error != null ? error.getMessage() : "no error message"), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSyncApplySuccess(String hash, String data) {
|
||||||
|
if (!hash.equalsIgnoreCase(Lbryio.lastRemoteHash)) {
|
||||||
|
SyncSetTask setTask = new SyncSetTask(Lbryio.lastRemoteHash, hash, data, null);
|
||||||
|
setTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSharedUserState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSyncApplyError(Exception error) {
|
||||||
|
// pass
|
||||||
|
Log.e(TAG, String.format("sync apply failed: %s", error != null ? error.getMessage() : "no error message"), error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
private void registerRequestsReceiver() {
|
private void registerRequestsReceiver() {
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(ACTION_AUTH_TOKEN_GENERATED);
|
intentFilter.addAction(ACTION_AUTH_TOKEN_GENERATED);
|
||||||
|
@ -979,11 +1141,15 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
showSignedInUser();
|
showSignedInUser();
|
||||||
|
|
||||||
if (requestCode == REQUEST_WALLET_SYNC_SIGN_IN) {
|
if (requestCode == REQUEST_WALLET_SYNC_SIGN_IN) {
|
||||||
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_WALLET_SYNC_ENABLED, true).apply();
|
||||||
|
|
||||||
for (Fragment fragment : openNavFragments.values()) {
|
for (Fragment fragment : openNavFragments.values()) {
|
||||||
if (fragment instanceof WalletFragment) {
|
if (fragment instanceof WalletFragment) {
|
||||||
((WalletFragment) fragment).onWalletSyncEnabled();
|
((WalletFragment) fragment).onWalletSyncEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scheduleWalletSyncTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1063,7 +1229,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
Lbryio.newInstall(context);
|
Lbryio.newInstall(context);
|
||||||
|
|
||||||
// (light) fetch subscriptions
|
// (light) fetch subscriptions
|
||||||
if (Lbryio.cacheSubscriptions.size() == 0) {
|
if (Lbryio.subscriptions.size() == 0) {
|
||||||
List<Subscription> subscriptions = new ArrayList<>();
|
List<Subscription> subscriptions = new ArrayList<>();
|
||||||
List<String> subUrls = new ArrayList<>();
|
List<String> subUrls = new ArrayList<>();
|
||||||
JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context));
|
JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context));
|
||||||
|
@ -1079,10 +1245,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
subscriptions.add(new Subscription(channelName, url.toString()));
|
subscriptions.add(new Subscription(channelName, url.toString()));
|
||||||
subUrls.add(url.toString());
|
subUrls.add(url.toString());
|
||||||
}
|
}
|
||||||
Lbryio.cacheSubscriptions = subscriptions;
|
Lbryio.subscriptions = subscriptions;
|
||||||
|
|
||||||
// resolve subscriptions
|
// resolve subscriptions
|
||||||
if (subUrls.size() > 0 && Lbryio.cacheResolvedSubscriptions.size() != Lbryio.cacheSubscriptions.size()) {
|
if (subUrls.size() > 0 && Lbryio.cacheResolvedSubscriptions.size() != Lbryio.subscriptions.size()) {
|
||||||
List<Claim> resolvedSubs = Lbry.resolve(subUrls, Lbry.LBRY_TV_CONNECTION_STRING);
|
List<Claim> resolvedSubs = Lbry.resolve(subUrls, Lbry.LBRY_TV_CONNECTION_STRING);
|
||||||
Lbryio.cacheResolvedSubscriptions = resolvedSubs;
|
Lbryio.cacheResolvedSubscriptions = resolvedSubs;
|
||||||
}
|
}
|
||||||
|
@ -1481,8 +1647,4 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
});
|
});
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadTags() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import java.sql.SQLInput;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -48,8 +49,8 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
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_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_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, is_followed FROM tags";
|
||||||
private static final String SQL_GET_KNOWN_TAGS = "SELECT name FROM tags";
|
private static final String SQL_UNFOLLOW_TAGS = "UPDATE tags SET is_followed = 0";
|
||||||
private static final String SQL_GET_FOLLOWED_TAGS = "SELECT name FROM tags WHERE is_followed = 1";
|
private static final String SQL_GET_FOLLOWED_TAGS = "SELECT name FROM tags WHERE is_followed = 1";
|
||||||
|
|
||||||
public DatabaseHelper(Context context) {
|
public DatabaseHelper(Context context) {
|
||||||
|
@ -108,8 +109,8 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
public static void createOrUpdateTag(Tag tag, SQLiteDatabase db) {
|
public static void createOrUpdateTag(Tag tag, SQLiteDatabase db) {
|
||||||
db.execSQL(SQL_INSERT_TAG, new Object[] { tag.getLowercaseName(), tag.isFollowed() ? 1 : 0 });
|
db.execSQL(SQL_INSERT_TAG, new Object[] { tag.getLowercaseName(), tag.isFollowed() ? 1 : 0 });
|
||||||
}
|
}
|
||||||
public static void setTagFollowed(boolean followed, String name, SQLiteDatabase db) {
|
public static void setAllTagsUnfollowed(SQLiteDatabase db) {
|
||||||
db.execSQL(SQL_SET_TAG_FOLLOWED, new Object[] { followed ? 1 : 0, name });
|
db.execSQL(SQL_UNFOLLOW_TAGS);
|
||||||
}
|
}
|
||||||
public static List<Tag> getTags(SQLiteDatabase db) {
|
public static List<Tag> getTags(SQLiteDatabase db) {
|
||||||
List<Tag> tags = new ArrayList<>();
|
List<Tag> tags = new ArrayList<>();
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package io.lbry.browser.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EditorsChoiceItem {
|
||||||
|
private boolean header;
|
||||||
|
private String title;
|
||||||
|
private String parent;
|
||||||
|
private String description;
|
||||||
|
private String thumbnailUrl;
|
||||||
|
private String permanentUrl;
|
||||||
|
|
||||||
|
public static EditorsChoiceItem fromClaim(Claim claim) {
|
||||||
|
EditorsChoiceItem item = new EditorsChoiceItem();
|
||||||
|
item.setTitle(claim.getTitle());
|
||||||
|
item.setDescription(claim.getDescription());
|
||||||
|
item.setThumbnailUrl(claim.getThumbnailUrl());
|
||||||
|
item.setPermanentUrl(claim.getPermanentUrl());
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,9 @@ public class Tag implements Comparator<Tag> {
|
||||||
return Predefined.MATURE_TAGS.contains(name.toLowerCase());
|
return Predefined.MATURE_TAGS.contains(name.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return getLowercaseName();
|
||||||
|
}
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return (o instanceof Tag) && ((Tag) o).getName().equalsIgnoreCase(name);
|
return (o instanceof Tag) && ((Tag) o).getName().equalsIgnoreCase(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package io.lbry.browser.model.lbryinc;
|
package io.lbry.browser.model.lbryinc;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
@Data
|
|
||||||
public class Subscription {
|
public class Subscription {
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String channelName;
|
private String channelName;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
public Subscription() {
|
public Subscription() {
|
||||||
|
@ -14,4 +18,11 @@ public class Subscription {
|
||||||
this.channelName = channelName;
|
this.channelName = channelName;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return (o instanceof Subscription) && url.equalsIgnoreCase(((Subscription) o).getUrl());
|
||||||
|
}
|
||||||
|
public int hashCode() {
|
||||||
|
return url.toLowerCase().hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ public class ChannelSubscribeTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
String action = isUnsubscribing ? "delete" : "new";
|
String action = isUnsubscribing ? "delete" : "new";
|
||||||
Lbryio.call("subscription", action, options, context);
|
Lbryio.call("subscription", action, options, context);
|
||||||
|
|
||||||
|
if (!isUnsubscribing) {
|
||||||
|
Lbryio.addSubscription(subscription);
|
||||||
|
} else {
|
||||||
|
Lbryio.removeSubscription(subscription);
|
||||||
|
}
|
||||||
} catch (LbryioRequestException | LbryioResponseException | SQLiteException ex) {
|
} catch (LbryioRequestException | LbryioResponseException | SQLiteException ex) {
|
||||||
error = ex;
|
error = ex;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class FetchSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (LbryioRequestException | LbryioResponseException | JSONException | ClassCastException ex) {
|
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | IllegalStateException ex) {
|
||||||
error = ex;
|
error = ex;
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
package io.lbry.browser.tasks;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import io.lbry.browser.MainActivity;
|
||||||
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.model.Tag;
|
||||||
|
import io.lbry.browser.utils.Lbry;
|
||||||
|
|
||||||
|
public class FollowUnfollowTagTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
private Tag tag;
|
||||||
|
private boolean unfollowing;
|
||||||
|
private Context context;
|
||||||
|
private FollowUnfollowTagHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public FollowUnfollowTagTask(Tag tag, boolean unfollowing, Context context, FollowUnfollowTagHandler handler) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.context = context;
|
||||||
|
this.unfollowing = unfollowing;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
public Boolean doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
db = ((MainActivity) context).getDbHelper().getWritableDatabase();
|
||||||
|
if (db != null) {
|
||||||
|
if (!Lbry.knownTags.contains(tag)) {
|
||||||
|
DatabaseHelper.createOrUpdateTag(tag, db);
|
||||||
|
Lbry.addKnownTag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.setFollowed(!unfollowing);
|
||||||
|
DatabaseHelper.createOrUpdateTag(tag, db);
|
||||||
|
if (unfollowing) {
|
||||||
|
Lbry.removeFollowedTag(tag);
|
||||||
|
} else {
|
||||||
|
Lbry.addFollowedTag(tag);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (handler != null) {
|
||||||
|
if (result) {
|
||||||
|
handler.onSuccess(tag, unfollowing);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface FollowUnfollowTagHandler {
|
||||||
|
void onSuccess(Tag tag, boolean unfollowing);
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
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.util.Log;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.lbry.browser.MainActivity;
|
||||||
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.exceptions.LbryUriException;
|
||||||
|
import io.lbry.browser.exceptions.LbryioRequestException;
|
||||||
|
import io.lbry.browser.exceptions.LbryioResponseException;
|
||||||
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.LbryUri;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
|
|
||||||
|
// background task to create a diff of local and remote subscriptions and try to merge
|
||||||
|
public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscription>> {
|
||||||
|
private static final String TAG = "MergeSubscriptionsTask";
|
||||||
|
private Context context;
|
||||||
|
private List<Subscription> base;
|
||||||
|
private List<Subscription> diff;
|
||||||
|
private MergeSubscriptionsHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public MergeSubscriptionsTask(List<Subscription> base, Context context, MergeSubscriptionsHandler handler) {
|
||||||
|
this.base = base;
|
||||||
|
this.context = context;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Subscription> doInBackground(Void... params) {
|
||||||
|
List<Subscription> combined = new ArrayList<>(base);
|
||||||
|
List<Subscription> localSubs = new ArrayList<>();
|
||||||
|
List<Subscription> remoteSubs = new ArrayList<>();
|
||||||
|
diff = new ArrayList<>();
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
try {
|
||||||
|
// fetch local subscriptions
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
db = ((MainActivity) context).getDbHelper().getWritableDatabase();
|
||||||
|
}
|
||||||
|
if (db != null) {
|
||||||
|
localSubs = DatabaseHelper.getSubscriptions(db);
|
||||||
|
for (Subscription sub : localSubs) {
|
||||||
|
if (!combined.contains(sub)) {
|
||||||
|
combined.add(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch remote subscriptions
|
||||||
|
JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("subscription", "list", context));
|
||||||
|
if (array != null) {
|
||||||
|
for (int i = 0; i < array.length(); i++) {
|
||||||
|
JSONObject item = array.getJSONObject(i);
|
||||||
|
String claimId = item.getString("claim_id");
|
||||||
|
String channelName = item.getString("channel_name");
|
||||||
|
|
||||||
|
LbryUri url = new LbryUri();
|
||||||
|
url.setChannelName(channelName);
|
||||||
|
url.setClaimId(claimId);
|
||||||
|
Subscription subscription = new Subscription(channelName, url.toString());
|
||||||
|
remoteSubs.add(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < combined.size(); i++) {
|
||||||
|
Subscription local = combined.get(i);
|
||||||
|
if (!remoteSubs.contains(local)) {
|
||||||
|
// add to remote subscriptions
|
||||||
|
try {
|
||||||
|
LbryUri uri = LbryUri.parse(local.getUrl());
|
||||||
|
Map<String, String> options = new HashMap<>();
|
||||||
|
options.put("claim_id", uri.getChannelClaimId());
|
||||||
|
options.put("channel_name", local.getChannelName());
|
||||||
|
Lbryio.call("subscription", "new", options, context);
|
||||||
|
} catch (LbryUriException | LbryioRequestException | LbryioResponseException ex) {
|
||||||
|
// pass
|
||||||
|
Log.e(TAG, String.format("subscription/new failed: %s", ex.getMessage()), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < localSubs.size(); i++) {
|
||||||
|
Subscription local = localSubs.get(i);
|
||||||
|
if (!base.contains(local) && !diff.contains(local)) {
|
||||||
|
diff.add(local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < remoteSubs.size(); i++) {
|
||||||
|
Subscription remote = remoteSubs.get(i);
|
||||||
|
if (!combined.contains(remote)) {
|
||||||
|
combined.add(remote);
|
||||||
|
if (!diff.contains(remote)) {
|
||||||
|
diff.add(remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | SQLiteException ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
Helper.closeDatabase(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
protected void onPostExecute(List<Subscription> subscriptions) {
|
||||||
|
if (handler != null) {
|
||||||
|
if (subscriptions != null) {
|
||||||
|
handler.onSuccess(subscriptions, diff);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MergeSubscriptionsHandler {
|
||||||
|
void onSuccess(List<Subscription> subscriptions, List<Subscription> diff);
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package io.lbry.browser.tasks.wallet;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteException;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.lbry.browser.MainActivity;
|
||||||
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.exceptions.ApiCallException;
|
||||||
|
import io.lbry.browser.exceptions.LbryUriException;
|
||||||
|
import io.lbry.browser.model.Tag;
|
||||||
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
|
import io.lbry.browser.utils.Lbry;
|
||||||
|
import io.lbry.browser.utils.LbryUri;
|
||||||
|
|
||||||
|
/*
|
||||||
|
version: '0.1',
|
||||||
|
value: {
|
||||||
|
subscriptions?: Array<string>,
|
||||||
|
tags?: Array<string>,
|
||||||
|
blocked?: Array<string>,
|
||||||
|
settings?: any,
|
||||||
|
app_welcome_version?: number,
|
||||||
|
sharing_3P?: boolean,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
public class LoadSharedUserStateTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
private static final String KEY = "shared";
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
private LoadSharedUserStateHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
private List<Subscription> subscriptions;
|
||||||
|
private List<Tag> followedTags;
|
||||||
|
|
||||||
|
public LoadSharedUserStateTask(Context context, LoadSharedUserStateHandler handler) {
|
||||||
|
this.context = context;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
// data to save
|
||||||
|
// current subscriptions
|
||||||
|
// Get the previous saved state
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PREFERENCE_GET, Lbry.buildSingleParam("key", KEY));
|
||||||
|
if (result != null) {
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
db = ((MainActivity) context).getDbHelper().getWritableDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject shared = result.getJSONObject("shared");
|
||||||
|
if (shared.has("type")
|
||||||
|
&& "object".equalsIgnoreCase(shared.getString("type"))
|
||||||
|
&& shared.has("value")) {
|
||||||
|
JSONObject value = shared.getJSONObject("value");
|
||||||
|
|
||||||
|
JSONArray subscriptionUrls =
|
||||||
|
value.has("subscriptions") && !value.isNull("subscriptions") ? value.getJSONArray("subscriptions") : null;
|
||||||
|
JSONArray tags =
|
||||||
|
value.has("tags") && !value.isNull("tags") ? value.getJSONArray("tags") : null;
|
||||||
|
|
||||||
|
if (subscriptionUrls != null) {
|
||||||
|
subscriptions = new ArrayList<>();
|
||||||
|
for (int i = 0; i < subscriptionUrls.length(); i++) {
|
||||||
|
String url = subscriptionUrls.getString(i);
|
||||||
|
try {
|
||||||
|
LbryUri uri = LbryUri.parse(LbryUri.normalize(url));
|
||||||
|
Subscription subscription = new Subscription();
|
||||||
|
subscription.setChannelName(uri.getChannelName());
|
||||||
|
subscription.setUrl(url);
|
||||||
|
subscriptions.add(subscription);
|
||||||
|
if (db != null) {
|
||||||
|
DatabaseHelper.createOrUpdateSubscription(subscription, db);
|
||||||
|
}
|
||||||
|
} catch (LbryUriException | SQLiteException ex) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags != null) {
|
||||||
|
if (db != null && tags.length() > 0) {
|
||||||
|
DatabaseHelper.setAllTagsUnfollowed(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
followedTags = new ArrayList<>();
|
||||||
|
for (int i = 0; i < tags.length(); i++) {
|
||||||
|
String tagName = tags.getString(i);
|
||||||
|
Tag tag = new Tag(tagName);
|
||||||
|
tag.setFollowed(true);
|
||||||
|
followedTags.add(tag);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (db != null) {
|
||||||
|
DatabaseHelper.createOrUpdateTag(tag, db);
|
||||||
|
}
|
||||||
|
} catch (SQLiteException ex) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (ApiCallException | JSONException ex) {
|
||||||
|
// failed
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (handler != null) {
|
||||||
|
if (result) {
|
||||||
|
handler.onSuccess(subscriptions, followedTags);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface LoadSharedUserStateHandler {
|
||||||
|
void onSuccess(List<Subscription> subscriptions, List<Tag> followedTags);
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package io.lbry.browser.tasks.wallet;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import io.lbry.browser.exceptions.ApiCallException;
|
||||||
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.Lbry;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
|
|
||||||
|
/*
|
||||||
|
version: '0.1',
|
||||||
|
value: {
|
||||||
|
subscriptions?: Array<string>,
|
||||||
|
tags?: Array<string>,
|
||||||
|
blocked?: Array<string>,
|
||||||
|
settings?: any,
|
||||||
|
app_welcome_version?: number,
|
||||||
|
sharing_3P?: boolean,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
public class SaveSharedUserStateTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
private static final String KEY = "shared";
|
||||||
|
private static final String VERSION = "0.1";
|
||||||
|
private SaveSharedUserStateHandler handler;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public SaveSharedUserStateTask(SaveSharedUserStateHandler handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
// data to save
|
||||||
|
// current subscriptions
|
||||||
|
List<String> subscriptionUrls = new ArrayList<>();
|
||||||
|
for (Subscription subscription : Lbryio.subscriptions) {
|
||||||
|
subscriptionUrls.add(subscription.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
// followed tags
|
||||||
|
List<String> followedTags = Helper.getTagsForTagObjects(Lbry.followedTags);
|
||||||
|
|
||||||
|
// Get the previous saved state
|
||||||
|
try {
|
||||||
|
boolean isExistingValid = false;
|
||||||
|
JSONObject sharedObject = null;
|
||||||
|
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_PREFERENCE_GET, Lbry.buildSingleParam("key", KEY));
|
||||||
|
if (result != null) {
|
||||||
|
JSONObject shared = result.getJSONObject("shared");
|
||||||
|
if (shared.has("type")
|
||||||
|
&& "object".equalsIgnoreCase(shared.getString("type"))
|
||||||
|
&& shared.has("value")) {
|
||||||
|
isExistingValid = true;
|
||||||
|
JSONObject value = shared.getJSONObject("value");
|
||||||
|
value.put("subscriptions", Helper.jsonArrayFromList(subscriptionUrls));
|
||||||
|
value.put("tags", Helper.jsonArrayFromList(followedTags));
|
||||||
|
sharedObject = shared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isExistingValid) {
|
||||||
|
// build a new object
|
||||||
|
JSONObject value = new JSONObject();
|
||||||
|
value.put("subscriptions", Helper.jsonArrayFromList(subscriptionUrls));
|
||||||
|
value.put("tags", Helper.jsonArrayFromList(followedTags));
|
||||||
|
|
||||||
|
sharedObject = new JSONObject();
|
||||||
|
sharedObject.put("type", "object");
|
||||||
|
sharedObject.put("value", value);
|
||||||
|
sharedObject.put("version", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put("key", KEY);
|
||||||
|
options.put("value", sharedObject.toString());
|
||||||
|
Lbry.genericApiCall(Lbry.METHOD_PREFERENCE_SET, options);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (ApiCallException | JSONException ex) {
|
||||||
|
// failed
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
if (handler != null) {
|
||||||
|
if (result) {
|
||||||
|
handler.onSuccess();
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SaveSharedUserStateHandler {
|
||||||
|
void onSuccess();
|
||||||
|
void onError(Exception error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package io.lbry.browser.tasks.wallet;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -24,8 +25,9 @@ public class SyncApplyTask extends AsyncTask<Void, Void, Boolean> {
|
||||||
private String syncHash;
|
private String syncHash;
|
||||||
private String syncData;
|
private String syncData;
|
||||||
|
|
||||||
public SyncApplyTask(boolean fetch, SyncTaskHandler handler) {
|
public SyncApplyTask(boolean fetch, String password, SyncTaskHandler handler) {
|
||||||
this.fetch = fetch;
|
this.fetch = fetch;
|
||||||
|
this.password = password;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class SyncGetTask extends AsyncTask<Void, Void, WalletSync> {
|
||||||
boolean unlockSuccessful =
|
boolean unlockSuccessful =
|
||||||
!isLocked || (boolean) Lbry.genericApiCall(Lbry.METHOD_WALLET_UNLOCK, Lbry.buildSingleParam("password", password));
|
!isLocked || (boolean) Lbry.genericApiCall(Lbry.METHOD_WALLET_UNLOCK, Lbry.buildSingleParam("password", password));
|
||||||
if (!unlockSuccessful) {
|
if (!unlockSuccessful) {
|
||||||
throw new WalletException("The wallet could be unlocked with the provided password.");
|
throw new WalletException("The wallet could not be unlocked with the provided password.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String hash = (String) Lbry.genericApiCall(Lbry.METHOD_SYNC_HASH);
|
String hash = (String) Lbry.genericApiCall(Lbry.METHOD_SYNC_HASH);
|
||||||
|
|
|
@ -33,14 +33,19 @@ import io.lbry.browser.dialog.CustomizeTagsDialogFragment;
|
||||||
import io.lbry.browser.model.Claim;
|
import io.lbry.browser.model.Claim;
|
||||||
import io.lbry.browser.model.Tag;
|
import io.lbry.browser.model.Tag;
|
||||||
import io.lbry.browser.tasks.ClaimSearchTask;
|
import io.lbry.browser.tasks.ClaimSearchTask;
|
||||||
|
import io.lbry.browser.tasks.FollowUnfollowTagTask;
|
||||||
|
import io.lbry.browser.tasks.GenericTaskHandler;
|
||||||
|
import io.lbry.browser.tasks.wallet.SaveSharedUserStateTask;
|
||||||
import io.lbry.browser.ui.BaseFragment;
|
import io.lbry.browser.ui.BaseFragment;
|
||||||
import io.lbry.browser.utils.Helper;
|
import io.lbry.browser.utils.Helper;
|
||||||
import io.lbry.browser.utils.Lbry;
|
import io.lbry.browser.utils.Lbry;
|
||||||
import io.lbry.browser.utils.Predefined;
|
import io.lbry.browser.utils.Predefined;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
// TODO: Similar code to FollowingFragment and Channel page fragment. Probably make common operations (sorting/filtering) into a control
|
// TODO: Similar code to FollowingFragment and Channel page fragment. Probably make common operations (sorting/filtering) into a control
|
||||||
public class AllContentFragment extends BaseFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class AllContentFragment extends BaseFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
|
@Getter
|
||||||
private boolean singleTagView;
|
private boolean singleTagView;
|
||||||
private List<String> tags;
|
private List<String> tags;
|
||||||
private View layoutFilterContainer;
|
private View layoutFilterContainer;
|
||||||
|
@ -55,6 +60,7 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
private RecyclerView contentList;
|
private RecyclerView contentList;
|
||||||
private int currentSortBy;
|
private int currentSortBy;
|
||||||
private int currentContentFrom;
|
private int currentContentFrom;
|
||||||
|
@Getter
|
||||||
private int currentContentScope;
|
private int currentContentScope;
|
||||||
private String contentReleaseTime;
|
private String contentReleaseTime;
|
||||||
private List<String> contentSortOrder;
|
private List<String> contentSortOrder;
|
||||||
|
@ -184,7 +190,6 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
});
|
});
|
||||||
|
|
||||||
checkParams(false);
|
checkParams(false);
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +209,12 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
titleView.setText(Helper.capitalize(tagName));
|
titleView.setText(Helper.capitalize(tagName));
|
||||||
} else {
|
} else {
|
||||||
singleTagView = false;
|
singleTagView = false;
|
||||||
tags = null;
|
// default to followed Tags scope if any tags are followed
|
||||||
|
tags = Helper.getTagsForTagObjects(Lbry.followedTags);
|
||||||
|
if (tags.size() > 0) {
|
||||||
|
currentContentScope = ContentScopeDialogFragment.ITEM_TAGS;
|
||||||
|
Helper.setViewVisibility(customizeLink, View.VISIBLE);
|
||||||
|
}
|
||||||
titleView.setText(getString(R.string.all_content));
|
titleView.setText(getString(R.string.all_content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,12 +265,16 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
public void onTagAdded(Tag tag) {
|
public void onTagAdded(Tag tag) {
|
||||||
// heavy-lifting
|
// heavy-lifting
|
||||||
// save to local, save to wallet and then sync
|
// save to local, save to wallet and then sync
|
||||||
|
FollowUnfollowTagTask task = new FollowUnfollowTagTask(tag, false, getContext(), followUnfollowHandler);
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTagRemoved(Tag tag) {
|
public void onTagRemoved(Tag tag) {
|
||||||
// heavy-lifting
|
// heavy-lifting
|
||||||
// save to local, save to wallet and then sync
|
// save to local, save to wallet and then sync
|
||||||
|
FollowUnfollowTagTask task = new FollowUnfollowTagTask(tag, true, getContext(), followUnfollowHandler);
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
|
@ -270,6 +284,30 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FollowUnfollowTagTask.FollowUnfollowTagHandler followUnfollowHandler = new FollowUnfollowTagTask.FollowUnfollowTagHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Tag tag, boolean unfollowing) {
|
||||||
|
if (tags != null) {
|
||||||
|
if (unfollowing) {
|
||||||
|
tags.remove(tag.getLowercaseName());
|
||||||
|
} else {
|
||||||
|
tags.add(tag.getLowercaseName());
|
||||||
|
}
|
||||||
|
fetchClaimSearchContent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
((MainActivity) context).saveSharedUserState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception error) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private void onSortByChanged(int sortBy) {
|
private void onSortByChanged(int sortBy) {
|
||||||
currentSortBy = sortBy;
|
currentSortBy = sortBy;
|
||||||
|
|
||||||
|
@ -364,7 +402,7 @@ public class AllContentFragment extends BaseFragment implements SharedPreference
|
||||||
fetchClaimSearchContent(false);
|
fetchClaimSearchContent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchClaimSearchContent(boolean reset) {
|
public void fetchClaimSearchContent(boolean reset) {
|
||||||
if (reset && contentListAdapter != null) {
|
if (reset && contentListAdapter != null) {
|
||||||
contentListAdapter.clearItems();
|
contentListAdapter.clearItems();
|
||||||
currentClaimSearchPage = 1;
|
currentClaimSearchPage = 1;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package io.lbry.browser.ui.editorschoice;
|
||||||
|
|
||||||
|
import io.lbry.browser.ui.BaseFragment;
|
||||||
|
|
||||||
|
public class EditorsChoiceFragment extends BaseFragment {
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
@ -333,16 +332,8 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
showSuggestedChannels();
|
showSuggestedChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Lbryio.cacheSubscriptions != null && Lbryio.cacheSubscriptions.size() > 0) {
|
if (Lbryio.subscriptions != null && Lbryio.subscriptions.size() > 0) {
|
||||||
subscriptionsList = new ArrayList<>(Lbryio.cacheSubscriptions);
|
fetchLoadedSubscriptions();
|
||||||
buildChannelIdsAndUrls();
|
|
||||||
if (Lbryio.cacheResolvedSubscriptions.size() > 0) {
|
|
||||||
updateChannelFilterListAdapter(Lbryio.cacheResolvedSubscriptions);
|
|
||||||
} else {
|
|
||||||
fetchAndResolveChannelList();
|
|
||||||
}
|
|
||||||
fetchClaimSearchContent();
|
|
||||||
showSubscribedContent();
|
|
||||||
} else {
|
} else {
|
||||||
fetchSubscriptions();
|
fetchSubscriptions();
|
||||||
}
|
}
|
||||||
|
@ -351,6 +342,17 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
|
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
public void fetchLoadedSubscriptions() {
|
||||||
|
subscriptionsList = new ArrayList<>(Lbryio.subscriptions);
|
||||||
|
buildChannelIdsAndUrls();
|
||||||
|
if (Lbryio.cacheResolvedSubscriptions.size() > 0) {
|
||||||
|
updateChannelFilterListAdapter(Lbryio.cacheResolvedSubscriptions);
|
||||||
|
} else {
|
||||||
|
fetchAndResolveChannelList();
|
||||||
|
}
|
||||||
|
fetchClaimSearchContent();
|
||||||
|
showSubscribedContent();
|
||||||
|
}
|
||||||
|
|
||||||
public void loadFollowing() {
|
public void loadFollowing() {
|
||||||
// wrapper to just re-fetch subscriptions (upon user sign in, for example)
|
// wrapper to just re-fetch subscriptions (upon user sign in, for example)
|
||||||
|
@ -662,7 +664,7 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
fetchSuggestedChannels();
|
fetchSuggestedChannels();
|
||||||
showSuggestedChannels();
|
showSuggestedChannels();
|
||||||
} else {
|
} else {
|
||||||
Lbryio.cacheSubscriptions = subscriptions;
|
Lbryio.subscriptions = subscriptions;
|
||||||
subscriptionsList = new ArrayList<>(subscriptions);
|
subscriptionsList = new ArrayList<>(subscriptions);
|
||||||
showSubscribedContent();
|
showSubscribedContent();
|
||||||
fetchAndResolveChannelList();
|
fetchAndResolveChannelList();
|
||||||
|
@ -686,6 +688,7 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
if (discoverDialog != null) {
|
if (discoverDialog != null) {
|
||||||
fetchSubscriptions();
|
fetchSubscriptions();
|
||||||
}
|
}
|
||||||
|
saveSharedUserState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -709,6 +712,7 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
if (discoverDialog != null) {
|
if (discoverDialog != null) {
|
||||||
fetchSubscriptions();
|
fetchSubscriptions();
|
||||||
}
|
}
|
||||||
|
saveSharedUserState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -729,6 +733,13 @@ public class FollowingFragment extends BaseFragment implements
|
||||||
Helper.setViewVisibility(noContentView, noContent ? View.VISIBLE : View.GONE);
|
Helper.setViewVisibility(noContentView, noContent ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveSharedUserState() {
|
||||||
|
Context context = getContext();
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
((MainActivity) context).saveSharedUserState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
|
||||||
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
if (key.equalsIgnoreCase(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT)) {
|
||||||
fetchClaimSearchContent(true);
|
fetchClaimSearchContent(true);
|
||||||
|
|
|
@ -188,7 +188,7 @@ public class WalletVerificationFragment extends Fragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processNewWallet() {
|
public void processNewWallet() {
|
||||||
SyncApplyTask fetchTask = new SyncApplyTask(true, new DefaultSyncTaskHandler() {
|
SyncApplyTask fetchTask = new SyncApplyTask(true, null, new DefaultSyncTaskHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSyncApplySuccess(String hash, String data) { createNewRemoteSync(hash, data); }
|
public void onSyncApplySuccess(String hash, String data) { createNewRemoteSync(hash, data); }
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -308,8 +308,6 @@ public class WalletFragment extends BaseFragment implements SdkStatusListener, W
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onWalletSyncEnabled() {
|
public void onWalletSyncEnabled() {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
|
|
||||||
sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_WALLET_SYNC_ENABLED, true).apply();
|
|
||||||
switchSyncStatus.setText(R.string.on);
|
switchSyncStatus.setText(R.string.on);
|
||||||
switchSyncStatus.setChecked(true);
|
switchSyncStatus.setChecked(true);
|
||||||
textWalletHintSyncStatus.setText(R.string.backup_synced);
|
textWalletHintSyncStatus.setText(R.string.backup_synced);
|
||||||
|
|
|
@ -316,7 +316,7 @@ public final class Helper {
|
||||||
}
|
}
|
||||||
public static List<Tag> filterFollowedTags(List<Tag> tags) {
|
public static List<Tag> filterFollowedTags(List<Tag> tags) {
|
||||||
List<Tag> followedTags = new ArrayList<>();
|
List<Tag> followedTags = new ArrayList<>();
|
||||||
for (Tag tag : followedTags) {
|
for (Tag tag : tags) {
|
||||||
if (tag.isFollowed()) {
|
if (tag.isFollowed()) {
|
||||||
followedTags.add(tag);
|
followedTags.add(tag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
public final class Lbry {
|
public final class Lbry {
|
||||||
|
private static final Object lock = new Object();
|
||||||
public static LinkedHashMap<ClaimCacheKey, Claim> claimCache = new LinkedHashMap<>();
|
public static LinkedHashMap<ClaimCacheKey, Claim> claimCache = new LinkedHashMap<>();
|
||||||
public static LinkedHashMap<Map<String, Object>, ClaimSearchCacheValue> claimSearchCache = new LinkedHashMap<>();
|
public static LinkedHashMap<Map<String, Object>, ClaimSearchCacheValue> claimSearchCache = new LinkedHashMap<>();
|
||||||
public static WalletBalance walletBalance = new WalletBalance();
|
public static WalletBalance walletBalance = new WalletBalance();
|
||||||
|
@ -85,7 +86,6 @@ public final class Lbry {
|
||||||
public static final String METHOD_PREFERENCE_GET = "preference_get";
|
public static final String METHOD_PREFERENCE_GET = "preference_get";
|
||||||
public static final String METHOD_PREFERENCE_SET = "preference_set";
|
public static final String METHOD_PREFERENCE_SET = "preference_set";
|
||||||
|
|
||||||
|
|
||||||
public static KeyStore KEYSTORE;
|
public static KeyStore KEYSTORE;
|
||||||
public static boolean SDK_READY = false;
|
public static boolean SDK_READY = false;
|
||||||
|
|
||||||
|
@ -165,8 +165,10 @@ public final class Lbry {
|
||||||
JSONObject json = new JSONObject(responseString);
|
JSONObject json = new JSONObject(responseString);
|
||||||
if (response.code() >= 200 && response.code() < 300) {
|
if (response.code() >= 200 && response.code() < 300) {
|
||||||
if (json.has("result")) {
|
if (json.has("result")) {
|
||||||
Object result = json.get("result");
|
if (json.isNull("result")) {
|
||||||
return result;
|
return null;
|
||||||
|
}
|
||||||
|
return json.get("result");
|
||||||
} else {
|
} else {
|
||||||
processErrorJson(json);
|
processErrorJson(json);
|
||||||
}
|
}
|
||||||
|
@ -417,4 +419,23 @@ public final class Lbry {
|
||||||
public static Object genericApiCall(String method) throws ApiCallException {
|
public static Object genericApiCall(String method) throws ApiCallException {
|
||||||
return genericApiCall(method, null);
|
return genericApiCall(method, null);
|
||||||
}
|
}
|
||||||
|
public static void addFollowedTag(Tag tag) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!followedTags.contains(tag)) {
|
||||||
|
followedTags.add(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void removeFollowedTag(Tag tag) {
|
||||||
|
synchronized (lock) {
|
||||||
|
followedTags.remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void addKnownTag(Tag tag) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!knownTags.contains(tag)) {
|
||||||
|
knownTags.add(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ public final class Lbryio {
|
||||||
public static final String TAG = "Lbryio";
|
public static final String TAG = "Lbryio";
|
||||||
public static final String CONNECTION_STRING = "https://api.lbry.com";
|
public static final String CONNECTION_STRING = "https://api.lbry.com";
|
||||||
public static final String AUTH_TOKEN_PARAM = "auth_token";
|
public static final String AUTH_TOKEN_PARAM = "auth_token";
|
||||||
public static List<Subscription> cacheSubscriptions = new ArrayList<>();
|
public static List<Subscription> subscriptions = new ArrayList<>();
|
||||||
public static List<Claim> cacheResolvedSubscriptions = new ArrayList<>();
|
public static List<Claim> cacheResolvedSubscriptions = new ArrayList<>();
|
||||||
public static double LBCUSDRate = 0;
|
public static double LBCUSDRate = 0;
|
||||||
public static String AUTH_TOKEN;
|
public static String AUTH_TOKEN;
|
||||||
|
@ -280,4 +280,17 @@ public final class Lbryio {
|
||||||
lastRemoteHash = hash;
|
lastRemoteHash = hash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void addSubscription(Subscription subscription) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!subscriptions.contains(subscription)) {
|
||||||
|
subscriptions.add(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void removeSubscription(Subscription subscription) {
|
||||||
|
synchronized (lock) {
|
||||||
|
subscriptions.remove(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
11
app/src/main/res/layout/fragment_editors_choice.xml
Normal file
11
app/src/main/res/layout/fragment_editors_choice.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?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">
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/editors_choice_content_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</LinearLayout>
|
61
app/src/main/res/layout/list_item_editors_choice.xml
Normal file
61
app/src/main/res/layout/list_item_editors_choice.xml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?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:id="@+id/editors_choice_header_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:clickable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/editors_choice_content_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/editors_choice_content_thumbnail"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_centerVertical="true"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/editors_choice_content_description"
|
||||||
|
android:layout_toRightOf="@id/editors_choice_content_thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="6"
|
||||||
|
android:textFontWeight="300"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -16,12 +16,14 @@
|
||||||
android:id="@+id/tag_action"
|
android:id="@+id/tag_action"
|
||||||
android:layout_width="16dp"
|
android:layout_width="16dp"
|
||||||
android:layout_height="16dp"
|
android:layout_height="16dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginRight="4dp"
|
android:layout_marginRight="4dp"
|
||||||
android:tint="@color/darkForeground" />
|
android:tint="@color/darkForeground" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tag_name"
|
android:id="@+id/tag_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
android:fontFamily="@font/inter"
|
android:fontFamily="@font/inter"
|
||||||
android:layout_marginLeft="4dp"
|
android:layout_marginLeft="4dp"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
|
Loading…
Reference in a new issue