diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ac0cf49..28eca8a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -56,13 +56,6 @@ - - - - - - - { - private static final String TAG = "LbryBufferEvent"; - private static final String ENDPOINT = "https://collector-service.api.lbry.tv/api/v1/events/video"; - - private String streamUrl; - private String userIdHash; - private long streamDuration; - private long streamPosition; - private long bufferDuration; - - public BufferEventTask(String streamUrl, long streamDuration, long streamPosition, long bufferDuration, String userIdHash) { - this.streamUrl = streamUrl; - this.bufferDuration = bufferDuration; - this.streamDuration = streamDuration; - this.streamPosition = streamPosition; - this.userIdHash = userIdHash; - } - - protected Void doInBackground(Void... params) { - JSONObject requestBody = new JSONObject(); - JSONObject data = new JSONObject(); - try { - data.put("url", streamUrl); - data.put("position", streamPosition); - data.put("stream_duration", streamDuration); - //data.put("duration", bufferDuration); - - requestBody.put("device", "android"); - requestBody.put("type", "buffering"); - requestBody.put("client", userIdHash); - requestBody.put("data", data); - - RequestBody body = RequestBody.create(requestBody.toString(), Helper.JSON_MEDIA_TYPE); - Request request = new Request.Builder().url(ENDPOINT).post(body).build(); - OkHttpClient client = new OkHttpClient.Builder(). - writeTimeout(60, TimeUnit.SECONDS). - readTimeout(60, TimeUnit.SECONDS). - build(); - - Response response = client.newCall(request).execute(); - String responseString = response.body().string(); - Log.d(TAG, String.format("buffer event sent: %s", responseString)); - } catch (Exception ex) { - // we don't want to fail if a buffer event fails to register - Log.d(TAG, String.format("buffer event log failed: %s", ex.getMessage()), ex); - } - - return null; - } -} diff --git a/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java b/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java index c82d728a..ff48f5d8 100644 --- a/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java @@ -60,12 +60,12 @@ public class MergeSubscriptionsTask extends AsyncTask remoteUnsubs = new ArrayList<>(); + List finalRemoteSubs = new ArrayList<>(); + if (remoteSubs.size() > 0) { + for (int i = 0; i < remoteSubs.size(); i++) { + Subscription sub = remoteSubs.get(i); + if (!combined.contains(sub)) { Map options = new HashMap<>(); - String channelClaimId = uri.getChannelClaimId(); - String channelName = Helper.normalizeChannelName(local.getChannelName()); - if (!Helper.isNullOrEmpty(channelClaimId) && !Helper.isNullOrEmpty(channelName)) { - options.put("claim_id", channelClaimId); - options.put("channel_name", channelName); - Lbryio.parseResponse(Lbryio.call("subscription", "new", options, context)); + LbryUri uri = LbryUri.tryParse(sub.getUrl()); + if (uri != null) { + options.put("claim_id", uri.getChannelClaimId()); + Lbryio.parseResponse(Lbryio.call("subscription", "delete", options, context)); + remoteUnsubs.add(sub); + } else { + finalRemoteSubs.add(sub); } - } catch (LbryUriException | LbryioRequestException | LbryioResponseException ex) { - // pass - Log.e(TAG, String.format("subscription/new failed: %s", ex.getMessage()), ex); } } } @@ -115,8 +114,8 @@ public class MergeSubscriptionsTask extends AsyncTask 0) { - // we only want to log a buffer event after the media has already started playing - String mediaSourceUrl = getStreamingUrl(); - long duration = MainActivity.appPlayer.getDuration(); - long position = MainActivity.appPlayer.getCurrentPosition(); - // TODO: Determine a hash for the userId - String userIdHash = Helper.SHA256(Lbryio.currentUser != null ? String.valueOf(Lbryio.currentUser.getId()) : "0"); - if (mediaSourceUrl.startsWith(CDN_PREFIX)) { - BufferEventTask bufferEvent = new BufferEventTask(claim.getPermanentUrl(), duration, position, 1, userIdHash); - bufferEvent.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } else { - // sdk stream buffer events should be handled differently - Bundle bundle = new Bundle(); - bundle.putString("url", claim.getPermanentUrl()); - bundle.putLong("stream_duration", duration); - bundle.putLong("stream_position", position); - bundle.putString("user_id_hash", userIdHash); - LbryAnalytics.logEvent(LbryAnalytics.EVENT_BUFFER, bundle); - } - } - showBuffering(); } else { hideBuffering(); @@ -749,44 +724,6 @@ public class FileViewFragment extends BaseFragment implements } } - private View.OnClickListener followUnfollowListener = new View.OnClickListener() { - @Override - public void onClick(View view) { - if (claim != null && claim.getSigningChannel() != null) { - Claim publisher = claim.getSigningChannel(); - boolean isFollowing = Lbryio.isFollowing(publisher); - Subscription subscription = Subscription.fromClaim(publisher); - view.setEnabled(false); - Context context = getContext(); - new ChannelSubscribeTask(context, publisher.getClaimId(), subscription, isFollowing, new ChannelSubscribeTask.ChannelSubscribeHandler() { - @Override - public void onSuccess() { - if (isFollowing) { - Lbryio.removeSubscription(subscription); - Lbryio.removeCachedResolvedSubscription(publisher); - } else { - Lbryio.addSubscription(subscription); - Lbryio.addCachedResolvedSubscription(publisher); - } - view.setEnabled(true); - checkIsFollowing(); - FollowingFragment.resetClaimSearchContent = true; - - // Save shared user state - if (context != null) { - context.sendBroadcast(new Intent(MainActivity.ACTION_SAVE_SHARED_USER_STATE)); - } - } - - @Override - public void onError(Exception exception) { - view.setEnabled(true); - } - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - }; - private void resolveUrl(String url) { resolving = true; Helper.setViewVisibility(layoutDisplayArea, View.INVISIBLE); @@ -1123,10 +1060,44 @@ public class FileViewFragment extends BaseFragment implements } }); - View buttonFollow = root.findViewById(R.id.file_view_icon_follow); - View buttonUnfollow = root.findViewById(R.id.file_view_icon_unfollow); - buttonFollow.setOnClickListener(followUnfollowListener); - buttonUnfollow.setOnClickListener(followUnfollowListener); + View buttonFollowUnfollow = root.findViewById(R.id.file_view_icon_follow_unfollow); + buttonFollowUnfollow.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (claim != null && claim.getSigningChannel() != null) { + Claim publisher = claim.getSigningChannel(); + boolean isFollowing = Lbryio.isFollowing(publisher); + Subscription subscription = Subscription.fromClaim(publisher); + buttonFollowUnfollow.setEnabled(false); + Context context = getContext(); + new ChannelSubscribeTask(context, publisher.getClaimId(), subscription, isFollowing, new ChannelSubscribeTask.ChannelSubscribeHandler() { + @Override + public void onSuccess() { + if (isFollowing) { + Lbryio.removeSubscription(subscription); + Lbryio.removeCachedResolvedSubscription(publisher); + } else { + Lbryio.addSubscription(subscription); + Lbryio.addCachedResolvedSubscription(publisher); + } + buttonFollowUnfollow.setEnabled(true); + checkIsFollowing(); + FollowingFragment.resetClaimSearchContent = true; + + // Save shared user state + if (context != null) { + context.sendBroadcast(new Intent(MainActivity.ACTION_SAVE_SHARED_USER_STATE)); + } + } + + @Override + public void onError(Exception exception) { + buttonFollowUnfollow.setEnabled(true); + } + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + }); commentChannelSpinnerAdapter = new InlineChannelSpinnerAdapter(getContext(), R.layout.spinner_item_channel, new ArrayList<>()); commentChannelSpinnerAdapter.addPlaceholder(false); @@ -1447,17 +1418,7 @@ public class FileViewFragment extends BaseFragment implements } } - boolean isAnonymous = claim.getSigningChannel() == null; - View iconFollow = root.findViewById(R.id.file_view_icon_follow); - View iconUnfollow = root.findViewById(R.id.file_view_icon_unfollow); - if (isAnonymous) { - if (iconFollow.getVisibility() == View.VISIBLE) { - iconFollow.setVisibility(View.INVISIBLE); - } - if (iconUnfollow.getVisibility() == View.VISIBLE) { - iconUnfollow.setVisibility(View.INVISIBLE); - } - } + root.findViewById(R.id.file_view_icon_follow_unfollow).setVisibility(claim.getSigningChannel() != null ? View.VISIBLE : View.GONE); MaterialButton mainActionButton = root.findViewById(R.id.file_view_main_action_button); if (claim.isPlayable()) { @@ -2447,10 +2408,11 @@ public class FileViewFragment extends BaseFragment implements Context context = getContext(); View root = getView(); if (context != null && root != null) { - OutlineIconView iconFollow = root.findViewById(R.id.file_view_icon_follow); - SolidIconView iconUnfollow = root.findViewById(R.id.file_view_icon_unfollow); - Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE: View.INVISIBLE); - Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.INVISIBLE); + SolidIconView iconFollowUnfollow = root.findViewById(R.id.file_view_icon_follow_unfollow); + if (iconFollowUnfollow != null) { + iconFollowUnfollow.setText(isFollowing ? R.string.fa_heart_broken : R.string.fa_heart); + iconFollowUnfollow.setTextColor(ContextCompat.getColor(context, isFollowing ? R.color.foreground : R.color.red)); + } } } } diff --git a/app/src/main/java/io/lbry/browser/utils/Helper.java b/app/src/main/java/io/lbry/browser/utils/Helper.java index eac70533..a2883ca2 100644 --- a/app/src/main/java/io/lbry/browser/utils/Helper.java +++ b/app/src/main/java/io/lbry/browser/utils/Helper.java @@ -30,8 +30,6 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.gms.common.util.Hex; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -39,9 +37,6 @@ import org.json.JSONObject; import java.io.Closeable; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -771,14 +766,4 @@ public final class Helper { } return id.toString(); } - - public static String SHA256(String value) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hash = digest.digest(value.getBytes("UTF-8")); - return Hex.bytesToStringLowercase(hash); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) { - return null; - } - } } diff --git a/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java b/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java index d5eadd7c..17b66b69 100644 --- a/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java +++ b/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java @@ -11,7 +11,6 @@ public class LbryAnalytics { public static final String EVENT_APP_ERROR = "app_error"; public static final String EVENT_APP_LAUNCH = "app_launch"; public static final String EVENT_COMMENT_CREATE = "comment_create"; - public static final String EVENT_BUFFER = "buffer"; public static final String EVENT_EMAIL_ADDED = "email_added"; public static final String EVENT_EMAIL_VERIFIED = "email_verified"; public static final String EVENT_FIRST_RUN_COMPLETED = "first_run_completed"; diff --git a/app/src/main/java/io/lbry/browser/utils/LbryUri.java b/app/src/main/java/io/lbry/browser/utils/LbryUri.java index 94458cfa..9e0790a7 100644 --- a/app/src/main/java/io/lbry/browser/utils/LbryUri.java +++ b/app/src/main/java/io/lbry/browser/utils/LbryUri.java @@ -17,8 +17,7 @@ public class LbryUri { public static final int CHANNEL_NAME_MIN_LENGTH = 1; public static final int CLAIM_ID_MAX_LENGTH = 40; - private static final String REGEX_PART_PROTOCOL = "^((?:lbry://|https://)?)"; - private static final String REGEX_PART_HOST = "((?:open.lbry.com/)?)"; + private static final String REGEX_PART_PROTOCOL = "^((?:lbry://)?)"; private static final String REGEX_PART_STREAM_OR_CHANNEL_NAME = "([^:$#/]*)"; private static final String REGEX_PART_MODIFIER_SEPARATOR = "([:$#]?)([^/]*)"; private static final String QUERY_STRING_BREAKER = "^([\\S]+)([?][\\S]*)"; @@ -59,9 +58,8 @@ public class LbryUri { return parse(url, false); } public static LbryUri parse(String url, boolean requireProto) throws LbryUriException { - Pattern componentsPattern = Pattern.compile(String.format("%s%s%s%s(/?)%s%s", + Pattern componentsPattern = Pattern.compile(String.format("%s%s%s(/?)%s%s", REGEX_PART_PROTOCOL, - REGEX_PART_HOST, REGEX_PART_STREAM_OR_CHANNEL_NAME, REGEX_PART_MODIFIER_SEPARATOR, REGEX_PART_STREAM_OR_CHANNEL_NAME, @@ -95,48 +93,37 @@ public class LbryUri { } // components[0] = proto - // components[1] = host - // components[2] = streamNameOrChannelName - // components[3] = primaryModSeparator - // components[4] = primaryModValue - // components[5] = pathSep - // components[6] = possibleStreamName - // components[7] = secondaryModSeparator - // components[8] = secondaryModValue + // components[1] = streamNameOrChannelName + // components[2] = primaryModSeparator + // components[3] = primaryModValue + // components[4] = pathSep + // components[5] = possibleStreamName + // components[6] = secondaryModSeparator + // components[7] = secondaryModValue if (requireProto && Helper.isNullOrEmpty(components.get(0))) { throw new LbryUriException("LBRY URLs must include a protocol prefix (lbry://)."); } - if (Helper.isNullOrEmpty(components.get(2))) { + if (Helper.isNullOrEmpty(components.get(1))) { throw new LbryUriException("URL does not include name."); } - for (String component : components.subList(2, components.size())) { + for (String component : components.subList(1, components.size())) { if (component.indexOf(' ') > -1) { throw new LbryUriException("URL cannot include a space."); } } - String streamOrChannelName = components.get(2); - String primaryModSeparator = components.get(3); - String primaryModValue = components.get(4); - String possibleStreamName = components.get(6); - String secondaryModSeparator = components.get(7); - String secondaryModValue = components.get(8); + String streamOrChannelName = components.get(1); + String primaryModSeparator = components.get(2); + String primaryModValue = components.get(3); + String possibleStreamName = components.get(5); + String secondaryModSeparator = components.get(6); + String secondaryModValue = components.get(7); boolean includesChannel = streamOrChannelName.startsWith("@"); boolean isChannel = includesChannel && Helper.isNullOrEmpty(possibleStreamName); String channelName = includesChannel && streamOrChannelName.length() > 1 ? streamOrChannelName.substring(1) : null; - - // It would have thrown already on the RegEx parser if protocol value was incorrect - // open.lbry.com uses ':' as ModSeparators while lbry:// expects '#' - if (components.get(1).equals("open.lbry.com/")) { - if (primaryModSeparator.equals(":")) - primaryModSeparator = "#"; - if (secondaryModSeparator.equals(":")) - secondaryModSeparator = "#"; - } - if (includesChannel) { if (Helper.isNullOrEmpty(channelName)) { throw new LbryUriException("No channel name after @."); @@ -160,7 +147,7 @@ public class LbryUri { LbryUri uri = new LbryUri(); uri.setChannel(isChannel); - uri.setPath(Helper.join(components.subList(2, components.size()), "")); + uri.setPath(Helper.join(components.subList(1, components.size()), "")); uri.setStreamName(streamName); uri.setStreamClaimId(streamClaimId); uri.setChannelName(channelName); diff --git a/app/src/main/res/layout/fragment_channel.xml b/app/src/main/res/layout/fragment_channel.xml index 7602a54a..af46977f 100644 --- a/app/src/main/res/layout/fragment_channel.xml +++ b/app/src/main/res/layout/fragment_channel.xml @@ -210,22 +210,14 @@ android:clickable="true" android:layout_width="36dp" android:layout_height="36dp"> - - diff --git a/app/src/main/res/layout/fragment_file_view.xml b/app/src/main/res/layout/fragment_file_view.xml index 1ff24b8e..ebac1449 100644 --- a/app/src/main/res/layout/fragment_file_view.xml +++ b/app/src/main/res/layout/fragment_file_view.xml @@ -484,7 +484,7 @@ android:paddingTop="12dp" android:paddingLeft="16dp" android:paddingBottom="12dp" - android:layout_toLeftOf="@id/file_view_icon_follow"> + android:layout_toLeftOf="@id/file_view_icon_follow_unfollow"> - - -