per-channel subscribe notification toggle

This commit is contained in:
Akinwale Ariwodola 2020-10-09 06:56:13 +01:00
parent 1877b75188
commit 746d442051
12 changed files with 203 additions and 42 deletions

View file

@ -2713,11 +2713,12 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
JSONObject item = array.getJSONObject(i); JSONObject item = array.getJSONObject(i);
String claimId = item.getString("claim_id"); String claimId = item.getString("claim_id");
String channelName = item.getString("channel_name"); String channelName = item.getString("channel_name");
boolean isNotificationsDisabled = item.getBoolean("is_notifications_disabled");
LbryUri url = new LbryUri(); LbryUri url = new LbryUri();
url.setChannelName(channelName); url.setChannelName(channelName);
url.setClaimId(claimId); url.setClaimId(claimId);
subscriptions.add(new Subscription(channelName, url.toString())); subscriptions.add(new Subscription(channelName, url.toString(), isNotificationsDisabled));
subUrls.add(url.toString()); subUrls.add(url.toString());
} }
Lbryio.subscriptions = subscriptions; Lbryio.subscriptions = subscriptions;

View file

@ -21,13 +21,13 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.LbryUri;
public class DatabaseHelper extends SQLiteOpenHelper { public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 7; public static final int DATABASE_VERSION = 8;
public static final String DATABASE_NAME = "LbryApp.db"; public static final String DATABASE_NAME = "LbryApp.db";
private static DatabaseHelper instance; private static DatabaseHelper instance;
private static final String[] SQL_CREATE_TABLES = { private static final String[] SQL_CREATE_TABLES = {
// local subscription store // local subscription store
"CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL)", "CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL, is_notifications_disabled INTEGER DEFAULT 0 NOT NULL)",
// url entry / suggestion history // url entry / suggestion history
"CREATE TABLE url_history (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL, url TEXT, type INTEGER NOT NULL, timestamp TEXT NOT NULL)", "CREATE TABLE url_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) // tags (known and followed)
@ -105,11 +105,15 @@ public class DatabaseHelper extends SQLiteOpenHelper {
"CREATE TABLE shuffle_watched (id INTEGER PRIMARY KEY NOT NULL, claim_id TEXT NOT NULL)", "CREATE TABLE shuffle_watched (id INTEGER PRIMARY KEY NOT NULL, claim_id TEXT NOT NULL)",
"CREATE UNIQUE INDEX idx_shuffle_watched_claim ON shuffle_watched (claim_id)" "CREATE UNIQUE INDEX idx_shuffle_watched_claim ON shuffle_watched (claim_id)"
}; };
private static final String[] SQL_V7_V8_UPGRADE = {
"AlTER TABLE subscriptions ADD COLUMN is_notifications_disabled INTEGER DEFAULT 0 NOT NULL"
};
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url) VALUES (?, ?)"; private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url, is_notifications_disabled) VALUES (?, ?, ?)";
private static final String SQL_UPDATE_SUBSCRIPTION_NOTIFICATION = "UPDATE subscriptions SET is_notification_disabled = ? WHERE url = ?";
private static final String SQL_CLEAR_SUBSCRIPTIONS = "DELETE FROM subscriptions"; private static final String SQL_CLEAR_SUBSCRIPTIONS = "DELETE FROM subscriptions";
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?"; 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_GET_SUBSCRIPTIONS = "SELECT channel_name, url, is_notifications_disabled FROM subscriptions";
private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)"; private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)";
private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history"; private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history";
@ -188,6 +192,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.execSQL(sql); db.execSQL(sql);
} }
} }
if (oldVersion < 8) {
for (String sql : SQL_V7_V8_UPGRADE) {
db.execSQL(sql);
}
}
} }
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
@ -304,7 +313,14 @@ public class DatabaseHelper extends SQLiteOpenHelper {
} }
public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) { public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) {
db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] { subscription.getChannelName(), subscription.getUrl() }); db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] {
subscription.getChannelName(),
subscription.getUrl(),
subscription.isNotificationsDisabled() ? 1 : 0
});
}
public static void setSubscriptionNotificationDisabled(boolean flag, String url, SQLiteDatabase db) {
db.execSQL(SQL_UPDATE_SUBSCRIPTION_NOTIFICATION, new Object[] { flag ? 1 : 0, url });
} }
public static void deleteSubscription(Subscription subscription, SQLiteDatabase db) { public static void deleteSubscription(Subscription subscription, SQLiteDatabase db) {
db.execSQL(SQL_DELETE_SUBSCRIPTION, new Object[] { subscription.getUrl() }); db.execSQL(SQL_DELETE_SUBSCRIPTION, new Object[] { subscription.getUrl() });
@ -321,6 +337,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
Subscription subscription = new Subscription(); Subscription subscription = new Subscription();
subscription.setChannelName(cursor.getString(0)); subscription.setChannelName(cursor.getString(0));
subscription.setUrl(cursor.getString(1)); subscription.setUrl(cursor.getString(1));
subscription.setNotificationsDisabled(cursor.getInt(2) == 1);
subscriptions.add(subscription); subscriptions.add(subscription);
} }
} finally { } finally {

View file

@ -11,17 +11,21 @@ public class Subscription {
@Getter @Getter
@Setter @Setter
private String url; private String url;
@Getter
@Setter
private boolean isNotificationsDisabled;
public Subscription() { public Subscription() {
} }
public Subscription(String channelName, String url) { public Subscription(String channelName, String url, boolean isNotificationsDisabled) {
this.channelName = channelName; this.channelName = channelName;
this.url = url; this.url = url;
this.isNotificationsDisabled = isNotificationsDisabled;
} }
public static Subscription fromClaim(Claim claim) { public static Subscription fromClaim(Claim claim) {
return new Subscription(claim.getName(), claim.getPermanentUrl()); return new Subscription(claim.getName(), claim.getPermanentUrl(), false);
} }
public String toString() { public String toString() {
return url; return url;

View file

@ -76,13 +76,15 @@ public class MergeSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
// check for any remote subs that may have been removed, and unsubscribe from them // check for any remote subs that may have been removed, and unsubscribe from them
for (int i = 0; i < array.length(); i++) { for (int i = 0; i < array.length(); i++) {
JSONObject item = array.getJSONObject(i); JSONObject item = array.getJSONObject(i);
// TODO: Refactor by creating static Subscription.fromJSON method
String claimId = item.getString("claim_id"); String claimId = item.getString("claim_id");
String channelName = item.getString("channel_name"); String channelName = item.getString("channel_name");
boolean isNotificationsDisabled = item.getBoolean("is_notifications_disabled");
LbryUri url = new LbryUri(); LbryUri url = new LbryUri();
url.setChannelName(channelName); url.setChannelName(channelName);
url.setClaimId(claimId); url.setClaimId(claimId);
Subscription subscription = new Subscription(channelName, url.toString()); Subscription subscription = new Subscription(channelName, url.toString(), isNotificationsDisabled);
remoteSubs.add(subscription); remoteSubs.add(subscription);
} }
} }

View file

@ -51,11 +51,11 @@ public class ChannelSubscribeTask extends AsyncTask<Void, Void, Boolean> {
options.put("claim_id", channelClaimId); options.put("claim_id", channelClaimId);
if (!isUnsubscribing) { if (!isUnsubscribing) {
options.put("channel_name", subscription.getChannelName()); options.put("channel_name", subscription.getChannelName());
options.put("notifications_disabled", String.valueOf(subscription.isNotificationsDisabled()).toLowerCase());
} }
String action = isUnsubscribing ? "delete" : "new"; String action = isUnsubscribing ? "delete" : "new";
Lbryio.call("subscription", action, options, context); Object response = Lbryio.parseResponse(Lbryio.call("subscription", action, options, context));
if (!isUnsubscribing) { if (!isUnsubscribing) {
Lbryio.addSubscription(subscription); Lbryio.addSubscription(subscription);
} else { } else {

View file

@ -49,11 +49,12 @@ public class FetchSubscriptionsTask extends AsyncTask<Void, Void, List<Subscript
JSONObject item = array.getJSONObject(i); JSONObject item = array.getJSONObject(i);
String claimId = item.getString("claim_id"); String claimId = item.getString("claim_id");
String channelName = item.getString("channel_name"); String channelName = item.getString("channel_name");
boolean isNotificationsDisabled = item.getBoolean("is_notifications_disabled");
LbryUri url = new LbryUri(); LbryUri url = new LbryUri();
url.setChannelName(channelName); url.setChannelName(channelName);
url.setClaimId(claimId); url.setClaimId(claimId);
Subscription subscription = new Subscription(channelName, url.toString()); Subscription subscription = new Subscription(channelName, url.toString(), isNotificationsDisabled);
subscriptions.add(subscription); subscriptions.add(subscription);
// Persist the subscription locally if it doesn't exist // Persist the subscription locally if it doesn't exist
if (db != null) { if (db != null) {

View file

@ -80,6 +80,8 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
private View buttonShare; private View buttonShare;
private View buttonTip; private View buttonTip;
private View buttonFollowUnfollow; private View buttonFollowUnfollow;
private View buttonBell;
private SolidIconView iconBell;
private int subCount; private int subCount;
private OutlineIconView iconFollow; private OutlineIconView iconFollow;
private SolidIconView iconUnfollow; private SolidIconView iconUnfollow;
@ -112,6 +114,8 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
buttonFollowUnfollow = root.findViewById(R.id.channel_view_follow_unfollow); buttonFollowUnfollow = root.findViewById(R.id.channel_view_follow_unfollow);
iconFollow = root.findViewById(R.id.channel_view_icon_follow); iconFollow = root.findViewById(R.id.channel_view_icon_follow);
iconUnfollow = root.findViewById(R.id.channel_view_icon_unfollow); iconUnfollow = root.findViewById(R.id.channel_view_icon_unfollow);
buttonBell = root.findViewById(R.id.channel_view_subscribe_notify);
iconBell = root.findViewById(R.id.channel_view_icon_bell);
tabPager = root.findViewById(R.id.channel_view_pager); tabPager = root.findViewById(R.id.channel_view_pager);
tabLayout = root.findViewById(R.id.channel_view_tabs); tabLayout = root.findViewById(R.id.channel_view_tabs);
@ -202,6 +206,39 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
} }
}); });
buttonBell.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (claim != null) {
boolean isNotificationsDisabled = Lbryio.isNotificationsDisabled(claim);
final Subscription subscription = Subscription.fromClaim(claim);
subscription.setNotificationsDisabled(!isNotificationsDisabled);
view.setEnabled(false);
Context context = getContext();
new ChannelSubscribeTask(context, claim.getClaimId(), subscription, false, new ChannelSubscribeTask.ChannelSubscribeHandler() {
@Override
public void onSuccess() {
view.setEnabled(true);
Lbryio.updateSubscriptionNotificationsDisabled(subscription);
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).showMessage(subscription.isNotificationsDisabled() ?
R.string.receive_no_notifications : R.string.receive_all_notifications);
}
checkIsFollowing();
}
@Override
public void onError(Exception exception) {
view.setEnabled(true);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
buttonFollowUnfollow.setOnClickListener(new View.OnClickListener() { buttonFollowUnfollow.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -213,7 +250,7 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
subscribing = true; subscribing = true;
boolean isFollowing = Lbryio.isFollowing(claim); boolean isFollowing = Lbryio.isFollowing(claim);
Subscription subscription = Subscription.fromClaim(claim); Subscription subscription = Subscription.fromClaim(claim);
buttonFollowUnfollow.setEnabled(false); view.setEnabled(false);
new ChannelSubscribeTask(getContext(), claim.getClaimId(), subscription, isFollowing, new ChannelSubscribeTask.ChannelSubscribeHandler() { new ChannelSubscribeTask(getContext(), claim.getClaimId(), subscription, isFollowing, new ChannelSubscribeTask.ChannelSubscribeHandler() {
@Override @Override
public void onSuccess() { public void onSuccess() {
@ -280,8 +317,14 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
private void checkIsFollowing() { private void checkIsFollowing() {
if (claim != null) { if (claim != null) {
boolean isFollowing = Lbryio.isFollowing(claim); boolean isFollowing = Lbryio.isFollowing(claim);
boolean notificationsDisabled = Lbryio.isNotificationsDisabled(claim);
Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE : View.GONE); Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.GONE); Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(buttonBell, isFollowing ? View.VISIBLE : View.GONE);
if (iconBell != null) {
iconBell.setText(notificationsDisabled ? R.string.fa_bell : R.string.fa_bell_slash);
}
} }
} }

View file

@ -785,6 +785,38 @@ public class FileViewFragment extends BaseFragment implements
} }
} }
private View.OnClickListener bellIconListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
if (claim != null && claim.getSigningChannel() != null) {
Claim publisher = claim.getSigningChannel();
boolean isNotificationsDisabled = Lbryio.isNotificationsDisabled(publisher);
final Subscription subscription = Subscription.fromClaim(publisher);
subscription.setNotificationsDisabled(!isNotificationsDisabled);
view.setEnabled(false);
Context context = getContext();
new ChannelSubscribeTask(context, publisher.getClaimId(), subscription, false, new ChannelSubscribeTask.ChannelSubscribeHandler() {
@Override
public void onSuccess() {
view.setEnabled(true);
Lbryio.updateSubscriptionNotificationsDisabled(subscription);
Context context = getContext();
if (context instanceof MainActivity) {
((MainActivity) context).showMessage(subscription.isNotificationsDisabled() ?
R.string.receive_no_notifications : R.string.receive_all_notifications);
}
checkIsFollowing();
}
@Override
public void onError(Exception exception) {
view.setEnabled(true);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
};
private View.OnClickListener followUnfollowListener = new View.OnClickListener() { private View.OnClickListener followUnfollowListener = new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
@ -1170,8 +1202,10 @@ public class FileViewFragment extends BaseFragment implements
View buttonFollow = root.findViewById(R.id.file_view_icon_follow); View buttonFollow = root.findViewById(R.id.file_view_icon_follow);
View buttonUnfollow = root.findViewById(R.id.file_view_icon_unfollow); View buttonUnfollow = root.findViewById(R.id.file_view_icon_unfollow);
View buttonBell = root.findViewById(R.id.file_view_icon_bell);
buttonFollow.setOnClickListener(followUnfollowListener); buttonFollow.setOnClickListener(followUnfollowListener);
buttonUnfollow.setOnClickListener(followUnfollowListener); buttonUnfollow.setOnClickListener(followUnfollowListener);
buttonBell.setOnClickListener(bellIconListener);
commentChannelSpinnerAdapter = new InlineChannelSpinnerAdapter(getContext(), R.layout.spinner_item_channel, new ArrayList<>()); commentChannelSpinnerAdapter = new InlineChannelSpinnerAdapter(getContext(), R.layout.spinner_item_channel, new ArrayList<>());
commentChannelSpinnerAdapter.addPlaceholder(false); commentChannelSpinnerAdapter.addPlaceholder(false);
@ -2524,13 +2558,18 @@ public class FileViewFragment extends BaseFragment implements
private void checkIsFollowing() { private void checkIsFollowing() {
if (claim != null && claim.getSigningChannel() != null) { if (claim != null && claim.getSigningChannel() != null) {
boolean isFollowing = Lbryio.isFollowing(claim.getSigningChannel()); boolean isFollowing = Lbryio.isFollowing(claim.getSigningChannel());
boolean notificationsDisabled = Lbryio.isNotificationsDisabled(claim.getSigningChannel());
Context context = getContext(); Context context = getContext();
View root = getView(); View root = getView();
if (context != null && root != null) { if (context != null && root != null) {
OutlineIconView iconFollow = root.findViewById(R.id.file_view_icon_follow); OutlineIconView iconFollow = root.findViewById(R.id.file_view_icon_follow);
SolidIconView iconUnfollow = root.findViewById(R.id.file_view_icon_unfollow); SolidIconView iconUnfollow = root.findViewById(R.id.file_view_icon_unfollow);
Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE: View.INVISIBLE); SolidIconView iconBell = root.findViewById(R.id.file_view_icon_bell);
Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.INVISIBLE); Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE: View.GONE);
Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(iconBell, isFollowing ? View.VISIBLE : View.GONE);
iconBell.setText(notificationsDisabled ? R.string.fa_bell : R.string.fa_bell_slash);
} }
} }
} }

View file

@ -333,6 +333,15 @@ public final class Lbryio {
} }
} }
public static void updateSubscriptionNotificationsDisabled(Subscription subscription) {
synchronized (lock) {
int index = subscriptions.indexOf(subscription);
if (index > -1) {
subscriptions.get(index).setNotificationsDisabled(subscription.isNotificationsDisabled());
}
}
}
public static void addSubscription(Subscription subscription) { public static void addSubscription(Subscription subscription) {
synchronized (lock) { synchronized (lock) {
if (!subscriptions.contains(subscription)) { if (!subscriptions.contains(subscription)) {
@ -364,6 +373,15 @@ public final class Lbryio {
public static boolean isFollowing(Claim claim) { public static boolean isFollowing(Claim claim) {
return subscriptions.contains(Subscription.fromClaim(claim)); return subscriptions.contains(Subscription.fromClaim(claim));
} }
public static boolean isNotificationsDisabled(Claim claim) {
Subscription sub = Subscription.fromClaim(claim);
int index = subscriptions.indexOf(sub);
if (index > -1) {
Subscription actual = subscriptions.get(subscriptions.indexOf(sub));
return actual.isNotificationsDisabled();
}
return false;
}
public static void updateRewardsLists(List<Reward> rewards) { public static void updateRewardsLists(List<Reward> rewards) {
synchronized (lock) { synchronized (lock) {

View file

@ -224,9 +224,27 @@
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:text="@string/fa_heart_broken" android:text="@string/fa_heart_broken"
android:textColor="@color/foreground"
android:textSize="20dp" android:textSize="20dp"
android:visibility="invisible"/> android:visibility="invisible"/>
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/channel_view_subscribe_notify"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginStart="8dp"
android:visibility="gone">
<io.lbry.browser.ui.controls.SolidIconView
android:id="@+id/channel_view_icon_bell"
android:layout_centerInParent="true"
android:layout_width="24dp"
android:layout_height="24dp"
android:text="@string/fa_bell_slash"
android:textColor="@color/foreground"
android:textSize="20dp"/>
</RelativeLayout>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View file

@ -503,7 +503,6 @@
android:id="@+id/file_view_publisher_area" android:id="@+id/file_view_publisher_area"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:id="@+id/file_view_publisher_info_area" android:id="@+id/file_view_publisher_info_area"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
@ -513,7 +512,7 @@
android:paddingTop="12dp" android:paddingTop="12dp"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingBottom="12dp" android:paddingBottom="12dp"
android:layout_toStartOf="@id/file_view_icon_follow"> android:layout_toStartOf="@id/file_view_publisher_area_actions">
<RelativeLayout <RelativeLayout
android:id="@+id/file_view_publisher_avatar" android:id="@+id/file_view_publisher_avatar"
android:layout_width="50dp" android:layout_width="50dp"
@ -570,12 +569,17 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/file_view_publisher_area_actions"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true">
<io.lbry.browser.ui.controls.OutlineIconView <io.lbry.browser.ui.controls.OutlineIconView
android:id="@+id/file_view_icon_follow" android:id="@+id/file_view_icon_follow"
android:clickable="true" android:clickable="true"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
@ -588,15 +592,27 @@
android:id="@+id/file_view_icon_unfollow" android:id="@+id/file_view_icon_unfollow"
android:clickable="true" android:clickable="true"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:text="@string/fa_heart_broken" android:text="@string/fa_heart_broken"
android:textColor="@color/foreground"
android:textSize="20dp" android:textSize="20dp"
android:visibility="invisible" /> android:visibility="gone" />
<io.lbry.browser.ui.controls.SolidIconView
android:id="@+id/file_view_icon_bell"
android:clickable="true"
android:background="?attr/selectableItemBackground"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:text="@string/fa_bell_slash"
android:textColor="@color/foreground"
android:textSize="20dp"
android:visibility="gone"/>
</LinearLayout>
</RelativeLayout> </RelativeLayout>
<View <View

View file

@ -117,6 +117,8 @@
<string name="content">Content</string> <string name="content">Content</string>
<string name="website">Website</string> <string name="website">Website</string>
<string name="reposted">reposted</string> <string name="reposted">reposted</string>
<string name="receive_all_notifications">You will receive all notifications</string>
<string name="receive_no_notifications">You will not receive notifications for this channel</string>
<plurals name="follower_count"> <plurals name="follower_count">
<item quantity="one">%1$s follower</item> <item quantity="one">%1$s follower</item>
<item quantity="other">%1$s followers</item> <item quantity="other">%1$s followers</item>