diff --git a/app/build.gradle b/app/build.gradle index b2abe1a9..b6a9fdbf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,8 +86,8 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - __32bitImplementation files('libs/lbrysdk-0.71.0-release__arm.aar') - __64bitImplementation files('libs/lbrysdk-0.71.0-release__arm64.aar') + __32bitImplementation files('libs/lbrysdk-0.72.0-release__arm.aar') + __64bitImplementation files('libs/lbrysdk-0.72.0-release__arm64.aar') } apply plugin: 'com.google.gms.google-services' diff --git a/app/libs/lbrysdk-0.71.0-release__arm.aar b/app/libs/lbrysdk-0.72.0-release__arm.aar similarity index 70% rename from app/libs/lbrysdk-0.71.0-release__arm.aar rename to app/libs/lbrysdk-0.72.0-release__arm.aar index b23902dc..4528d295 100644 Binary files a/app/libs/lbrysdk-0.71.0-release__arm.aar and b/app/libs/lbrysdk-0.72.0-release__arm.aar differ diff --git a/app/libs/lbrysdk-0.71.0-release__arm64.aar b/app/libs/lbrysdk-0.72.0-release__arm64.aar similarity index 71% rename from app/libs/lbrysdk-0.71.0-release__arm64.aar rename to app/libs/lbrysdk-0.72.0-release__arm64.aar index 32e37013..c35f7be5 100644 Binary files a/app/libs/lbrysdk-0.71.0-release__arm64.aar and b/app/libs/lbrysdk-0.72.0-release__arm64.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa041963..b5cb22f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,19 @@ android:supportsRtl="true" android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> + + + + + + + - + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/io/lbry/browser/FileViewActivity.java b/app/src/main/java/io/lbry/browser/FileViewActivity.java index 881029b5..628f244f 100644 --- a/app/src/main/java/io/lbry/browser/FileViewActivity.java +++ b/app/src/main/java/io/lbry/browser/FileViewActivity.java @@ -1,29 +1,39 @@ package io.lbry.browser; +import android.annotation.SuppressLint; import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.graphics.Color; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.service.voice.VoiceInteractionSession; import android.text.format.DateUtils; import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.widget.ImageView; import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.widget.NestedScrollView; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; @@ -34,12 +44,17 @@ import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.util.Util; import com.google.android.flexbox.FlexboxLayoutManager; +import com.google.android.material.button.MaterialButton; import com.google.android.material.snackbar.Snackbar; import java.math.BigDecimal; import java.text.DecimalFormat; +import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import io.lbry.browser.adapter.ClaimListAdapter; import io.lbry.browser.adapter.TagListAdapter; @@ -47,15 +62,22 @@ import io.lbry.browser.dialog.SendTipDialogFragment; import io.lbry.browser.exceptions.LbryUriException; import io.lbry.browser.model.Claim; import io.lbry.browser.model.ClaimCacheKey; +import io.lbry.browser.model.Fee; import io.lbry.browser.model.File; import io.lbry.browser.model.Tag; +import io.lbry.browser.model.lbryinc.Reward; import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ClaimSearchTask; import io.lbry.browser.tasks.FileListTask; +import io.lbry.browser.tasks.GenericTaskHandler; import io.lbry.browser.tasks.LighthouseSearchTask; import io.lbry.browser.tasks.ResolveTask; +import io.lbry.browser.tasks.lbryinc.ClaimRewardTask; +import io.lbry.browser.tasks.lbryinc.FetchStatCountTask; +import io.lbry.browser.tasks.lbryinc.LogFileViewTask; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; public class FileViewActivity extends AppCompatActivity { @@ -65,16 +87,23 @@ public class FileViewActivity extends AppCompatActivity { private static final int SHARE_REQUEST_CODE = 3001; private static boolean startingShareActivity; - private SimpleExoPlayer player; private boolean hasLoadedFirstBalance; private boolean loadFilePending; private boolean resolving; private Claim claim; + private String currentUrl; private ClaimListAdapter relatedContentAdapter; private File file; private BroadcastReceiver sdkReceiver; private Player.EventListener fileViewPlayerListener; + private long elapsedDuration = 0; + private long totalDuration = 0; + private boolean elapsedPlaybackScheduled; + private ScheduledExecutorService elapsedPlaybackScheduler; + private boolean playbackStarted; + private long startTimeMillis; + private View buttonShareAction; private View buttonTipAction; private View buttonRepostAction; @@ -118,6 +147,8 @@ public class FileViewActivity extends AppCompatActivity { } setContentView(R.layout.activity_file_view); + currentUrl = url; + logUrlEvent(url); if (claim == null) { resolveUrl(url); } @@ -127,9 +158,23 @@ public class FileViewActivity extends AppCompatActivity { fileViewPlayerListener = new Player.EventListener() { @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { - /*if (playbackState == Player.STATE_READY) { - MainActivity.setNowPlayingClaim(claim, FileViewActivity.this); - }*/ + if (playbackState == Player.STATE_READY) { + if (totalDuration == 0) { + elapsedDuration = MainActivity.appPlayer.getCurrentPosition(); + totalDuration = MainActivity.appPlayer.getDuration(); + } + if (!playbackStarted) { + logPlay(currentUrl, startTimeMillis); + playbackStarted = true; + } + renderTotalDuration(); + scheduleElapsedPlayback(); + hideBuffering(); + } else if (playbackState == Player.STATE_BUFFERING) { + showBuffering(); + } else { + hideBuffering(); + } } }; @@ -138,6 +183,12 @@ public class FileViewActivity extends AppCompatActivity { renderClaim(); } + private void logUrlEvent(String url) { + Bundle bundle = new Bundle(); + bundle.putString("uri", url); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_OPEN_FILE_PAGE, bundle); + } + private void checkAndResetNowPlayingClaim() { if (MainActivity.nowPlayingClaim != null && !MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId())) { @@ -164,6 +215,9 @@ public class FileViewActivity extends AppCompatActivity { return; } + currentUrl = newUrl; + logUrlEvent(newUrl); + resetViewCount(); ClaimCacheKey key = new ClaimCacheKey(); key.setClaimId(newClaimId); if (!Helper.isNullOrEmpty(newUrl) && newUrl.contains("#")) { @@ -230,7 +284,7 @@ public class FileViewActivity extends AppCompatActivity { } private String buildLbryTvStreamingUrl() { - return String.format("https://player.lbry.tv/content/claims/%s/%s/stream", claim.getName(), claim.getClaimId()); + return String.format("https://cdn.lbryplayer.xyz/content/claims/%s/%s/stream", claim.getName(), claim.getClaimId()); } private void loadFile() { @@ -241,7 +295,7 @@ public class FileViewActivity extends AppCompatActivity { } loadFilePending = false; - // TODO: Check if it's paid content and then wait for the user to explicity request the file + // TODO: Check if it's paid content and then wait for the user to explicitly request the file String claimId = claim.getClaimId(); FileListTask task = new FileListTask(claimId, null, new FileListTask.FileListResultHandler() { @Override @@ -257,6 +311,7 @@ public class FileViewActivity extends AppCompatActivity { } }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } protected void onResume() { @@ -266,6 +321,7 @@ public class FileViewActivity extends AppCompatActivity { private void resolveUrl(String url) { resolving = true; + findViewById(R.id.file_view_claim_display_area).setVisibility(View.INVISIBLE); View loadingView = findViewById(R.id.file_view_loading_container); ResolveTask task = new ResolveTask(url, Lbry.LBRY_TV_CONNECTION_STRING, loadingView, new ClaimListResultHandler() { @Override @@ -359,6 +415,18 @@ public class FileViewActivity extends AppCompatActivity { } }); + findViewById(R.id.player_toggle_full_screen).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // check full screen mode + if (isInFullscreenMode()) { + disableFullScreenMode(); + } else { + enableFullScreenMode(); + } + } + }); + RecyclerView relatedContentList = findViewById(R.id.file_view_related_content_list); relatedContentList.setNestedScrollingEnabled(false); LinearLayoutManager llm = new LinearLayoutManager(this); @@ -370,6 +438,8 @@ public class FileViewActivity extends AppCompatActivity { return; } + loadViewCount(); + ((NestedScrollView) findViewById(R.id.file_view_scroll_view)).scrollTo(0, 0); findViewById(R.id.file_view_claim_display_area).setVisibility(View.VISIBLE); @@ -402,27 +472,58 @@ public class FileViewActivity extends AppCompatActivity { descTagsList.setAdapter(tagListAdapter); findViewById(R.id.file_view_tag_area).setVisibility(tags.size() > 0 ? View.VISIBLE : View.GONE); + findViewById(R.id.file_view_exoplayer_container).setVisibility(View.GONE); + findViewById(R.id.file_view_unsupported_container).setVisibility(View.GONE); + findViewById(R.id.file_view_media_meta_container).setVisibility(View.VISIBLE); + Claim.GenericMetadata metadata = claim.getValue(); + if (!Helper.isNullOrEmpty(claim.getThumbnailUrl())) { + ImageView thumbnailView = findViewById(R.id.file_view_thumbnail); + Glide.with(getApplicationContext()).load(claim.getThumbnailUrl()).centerCrop().into(thumbnailView); + } else { + // display first x letters of claim name, with random background + } + + findViewById(R.id.file_view_main_action_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onMainActionButtonClicked(); + } + }); + findViewById(R.id.file_view_media_meta_container).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onMainActionButtonClicked(); + } + }); + + boolean isFree = true; if (metadata instanceof Claim.StreamMetadata) { Claim.StreamMetadata streamMetadata = (Claim.StreamMetadata) metadata; long publishTime = streamMetadata.getReleaseTime() > 0 ? streamMetadata.getReleaseTime() * 1000 : claim.getTimestamp() * 1000; ((TextView) findViewById(R.id.file_view_publish_time)).setText(DateUtils.getRelativeTimeSpanString( publishTime, System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE)); - // Check the metadata type - String mediaType = streamMetadata.getSource().getMediaType(); - // Use Exoplayer view if it's video / audio - if (mediaType.startsWith("audio") || mediaType.startsWith("video")) { - showExoplayerView(); - playMedia(); - } else if (mediaType.startsWith("text")) { - - } else if (mediaType.startsWith("image")) { - - } else { - // unsupported type - showUnsupportedView(); + Fee fee = streamMetadata.getFee(); + if (fee != null && Helper.parseDouble(fee.getAmount(), 0) > 0) { + isFree = false; + findViewById(R.id.file_view_fee_container).setVisibility(View.VISIBLE); + ((TextView) findViewById(R.id.file_view_fee)).setText(Helper.shortCurrencyFormat(Helper.parseDouble(fee.getAmount(), 0))); } + + MaterialButton mainActionButton = findViewById(R.id.file_view_main_action_button); + String mediaType = streamMetadata.getSource().getMediaType(); + if (mediaType.startsWith("audio") || mediaType.startsWith("video")) { + mainActionButton.setText(R.string.play); + } else if (mediaType.startsWith("text") || mediaType.startsWith("image")) { + mainActionButton.setText(R.string.view); + } else { + mainActionButton.setText(R.string.download); + } + } + + if (isFree) { + onMainActionButtonClicked(); } loadRelatedContent(); @@ -451,9 +552,7 @@ public class FileViewActivity extends AppCompatActivity { } PlayerView view = findViewById(R.id.file_view_exoplayer_view); - PlayerControlView controlView = findViewById(R.id.file_view_exoplayer_control_view); view.setPlayer(MainActivity.appPlayer); - controlView.setPlayer(MainActivity.appPlayer); if (MainActivity.nowPlayingClaim != null && MainActivity.nowPlayingClaim.getClaimId().equalsIgnoreCase(claim.getClaimId()) && @@ -462,6 +561,8 @@ public class FileViewActivity extends AppCompatActivity { return; } + resetPlayer(); + showBuffering(); MainActivity.setNowPlayingClaim(claim, FileViewActivity.this); String userAgent = Util.getUserAgent(this, getString(R.string.app_name)); MediaSource mediaSource = new ProgressiveMediaSource.Factory( @@ -471,8 +572,81 @@ public class FileViewActivity extends AppCompatActivity { MainActivity.appPlayer.prepare(mediaSource, true, true); } - private void loadViewCount() { + private void resetViewCount() { + TextView textViewCount = findViewById(R.id.file_view_view_count); + Helper.setViewText(textViewCount, null); + Helper.setViewVisibility(textViewCount, View.GONE); + } + private void loadViewCount() { + if (claim != null) { + FetchStatCountTask task = new FetchStatCountTask( + FetchStatCountTask.STAT_VIEW_COUNT, claim.getClaimId(), null, new FetchStatCountTask.FetchStatCountHandler() { + @Override + public void onSuccess(int count) { + try { + String displayText = getResources().getQuantityString(R.plurals.view_count, count, NumberFormat.getInstance().format(count)); + TextView textViewCount = findViewById(R.id.file_view_view_count); + Helper.setViewText(textViewCount, displayText); + Helper.setViewVisibility(textViewCount, View.VISIBLE); + } catch (IllegalStateException ex) { + // pass + } + } + + @Override + public void onError(Exception error) { + // pass + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void onMainActionButtonClicked() { + // Check if the claim is free + Claim.GenericMetadata metadata = claim.getValue(); + if (metadata instanceof Claim.StreamMetadata) { + Claim.StreamMetadata streamMetadata = (Claim.StreamMetadata) metadata; + + Fee fee = streamMetadata.getFee(); + if (fee != null && Helper.parseDouble(fee.getAmount(), 0) > 0) { + // not free, perform a purchase + + } else { + handleMainActionForClaim(); + } + } else { + showError(getString(R.string.cannot_view_claim)); + } + } + + private void handleMainActionForClaim() { + startTimeMillis = System.currentTimeMillis(); + Claim.GenericMetadata metadata = claim.getValue(); + if (metadata instanceof Claim.StreamMetadata) { + Claim.StreamMetadata streamMetadata = (Claim.StreamMetadata) metadata; + // Check the metadata type + String mediaType = streamMetadata.getSource().getMediaType(); + // Use Exoplayer view if it's video / audio + if (mediaType.startsWith("audio") || mediaType.startsWith("video")) { + showExoplayerView(); + playMedia(); + } else if (mediaType.startsWith("text")) { + + } else if (mediaType.startsWith("image")) { + + } else { + // unsupported type + showUnsupportedView(); + } + } else { + showError(getString(R.string.cannot_view_claim)); + } + } + + private void showError(String message) { + Snackbar.make(findViewById(R.id.file_view_claim_display_area), message, Snackbar.LENGTH_LONG).setBackgroundTint(Color.RED).show(); } private void loadRelatedContent() { @@ -524,6 +698,11 @@ public class FileViewActivity extends AppCompatActivity { } public void onBackPressed() { + if (isInFullscreenMode()) { + disableFullScreenMode(); + return; + } + MainActivity.mainActive = true; Intent intent = new Intent(this, MainActivity.class); startActivity(intent); @@ -567,16 +746,13 @@ public class FileViewActivity extends AppCompatActivity { private void renderPictureInPictureMode() { findViewById(R.id.file_view_scroll_view).setVisibility(View.GONE); - findViewById(R.id.file_view_exoplayer_control_view).setVisibility(View.GONE); findViewById(R.id.floating_balance_main_container).setVisibility(View.GONE); } private void renderFullMode() { findViewById(R.id.file_view_scroll_view).setVisibility(View.VISIBLE); - findViewById(R.id.floating_balance_main_container).setVisibility(View.VISIBLE); - - PlayerControlView controlView = findViewById(R.id.file_view_exoplayer_control_view); - controlView.setPlayer(null); - controlView.setPlayer(MainActivity.appPlayer); + if (!isInFullscreenMode()) { + findViewById(R.id.floating_balance_main_container).setVisibility(View.VISIBLE); + } } @Override @@ -587,4 +763,151 @@ public class FileViewActivity extends AppCompatActivity { renderFullMode(); } } + + @SuppressLint("SourceLockedOrientationActivity") + private void enableFullScreenMode() { + findViewById(R.id.floating_balance_main_container).setVisibility(View.INVISIBLE); + + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + + ConstraintLayout globalLayout = findViewById(R.id.file_view_global_layout); + View exoplayerContainer = findViewById(R.id.file_view_exoplayer_container); + ((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer); + globalLayout.addView(exoplayerContainer); + + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + ((ImageView) findViewById(R.id.player_image_full_screen_toggle)).setImageResource(R.drawable.ic_fullscreen_exit); + } + + private void disableFullScreenMode() { + RelativeLayout mediaContainer = findViewById(R.id.file_view_media_container); + View exoplayerContainer = findViewById(R.id.file_view_exoplayer_container); + ((ViewGroup) exoplayerContainer.getParent()).removeView(exoplayerContainer); + mediaContainer.addView(exoplayerContainer); + + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); + + ((ImageView) findViewById(R.id.player_image_full_screen_toggle)).setImageResource(R.drawable.ic_fullscreen); + findViewById(R.id.floating_balance_main_container).setVisibility(View.VISIBLE); + } + + private boolean isInFullscreenMode() { + View exoplayerContainer = findViewById(R.id.file_view_exoplayer_container); + return exoplayerContainer.getParent() instanceof ConstraintLayout; + } + + private void scheduleElapsedPlayback() { + if (!elapsedPlaybackScheduled) { + elapsedPlaybackScheduler = Executors.newSingleThreadScheduledExecutor(); + elapsedPlaybackScheduler.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (MainActivity.appPlayer != null) { + elapsedDuration = MainActivity.appPlayer.getCurrentPosition(); + renderElapsedDuration(); + } + } + }); + } + }, 0, 500, TimeUnit.MILLISECONDS); + elapsedPlaybackScheduled = true; + } + } + + private void resetPlayer() { + elapsedDuration = 0; + totalDuration = 0; + elapsedPlaybackScheduled = false; + if (elapsedPlaybackScheduler != null) { + elapsedPlaybackScheduler.shutdownNow(); + elapsedPlaybackScheduler = null; + } + + playbackStarted = false; + startTimeMillis = 0; + } + + private void showBuffering() { + findViewById(R.id.player_buffering_progress).setVisibility(View.VISIBLE); + } + + private void hideBuffering() { + findViewById(R.id.player_buffering_progress).setVisibility(View.INVISIBLE); + } + + private void renderElapsedDuration() { + Helper.setViewText(findViewById(R.id.player_duration_elapsed), Helper.formatDuration(Double.valueOf(elapsedDuration / 1000.0).longValue())); + } + + private void renderTotalDuration() { + Helper.setViewText(findViewById(R.id.player_duration_total), Helper.formatDuration(Double.valueOf(totalDuration / 1000.0).longValue())); + } + + private void logPlay(String url, long startTimeMillis) { + long timeToStartMillis = startTimeMillis > 0 ? System.currentTimeMillis() - startTimeMillis : 0; + + Bundle bundle = new Bundle(); + bundle.putString("uri", url); + bundle.putLong("time_to_start_ms", timeToStartMillis); + bundle.putLong("time_to_start_seconds", Double.valueOf(timeToStartMillis / 1000.0).longValue()); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_PLAY, bundle); + + logFileView(url, timeToStartMillis); + } + + private void logFileView(String url, long timeToStart) { + if (claim != null) { + LogFileViewTask task = new LogFileViewTask(url, claim, timeToStart, new GenericTaskHandler() { + @Override + public void beforeStart() { } + + @Override + public void onSuccess() { + claimEligibleRewards(); + } + + @Override + public void onError(Exception error) { } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + private void claimEligibleRewards() { + // attempt to claim eligible rewards after viewing or playing a file (fail silently) + ClaimRewardTask firstStreamTask = new ClaimRewardTask(Reward.TYPE_FIRST_STREAM, null, null, this, eligibleRewardHandler); + ClaimRewardTask dailyViewTask = new ClaimRewardTask(Reward.TYPE_DAILY_VIEW, null, null, this, eligibleRewardHandler); + firstStreamTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + dailyViewTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private ClaimRewardTask.ClaimRewardHandler eligibleRewardHandler = new ClaimRewardTask.ClaimRewardHandler() { + @Override + public void onSuccess(double amountClaimed, String message) { + if (Helper.isNullOrEmpty(message)) { + message = getResources().getQuantityString( + R.plurals.claim_reward_message, + amountClaimed == 1 ? 1 : 2, + new DecimalFormat(Helper.LBC_CURRENCY_FORMAT_PATTERN).format(amountClaimed)); + } + Snackbar.make(findViewById(R.id.file_view_global_layout), message, Snackbar.LENGTH_LONG).show(); + } + + @Override + public void onError(Exception error) { + // pass + } + }; } diff --git a/app/src/main/java/io/lbry/browser/FirstRunActivity.java b/app/src/main/java/io/lbry/browser/FirstRunActivity.java index 0aa9a0d2..8197fd70 100644 --- a/app/src/main/java/io/lbry/browser/FirstRunActivity.java +++ b/app/src/main/java/io/lbry/browser/FirstRunActivity.java @@ -7,7 +7,6 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; -import android.text.Html; import android.text.method.LinkMovementMethod; import android.view.View; import android.widget.TextView; @@ -19,6 +18,7 @@ import androidx.preference.PreferenceManager; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.Lbryio; public class FirstRunActivity extends AppCompatActivity { @@ -60,6 +60,11 @@ public class FirstRunActivity extends AppCompatActivity { } } + public void onResume() { + super.onResume(); + LbryAnalytics.setCurrentScreen(this, "First Run", "FirstRun"); + } + private void registerAuthReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(MainActivity.ACTION_USER_AUTHENTICATION_SUCCESS); @@ -78,8 +83,13 @@ public class FirstRunActivity extends AppCompatActivity { } private void handleAuthenticationSuccess() { - // first_auth completed event + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + boolean firstAuthCompleted = sp.getBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_FIRST_AUTH_COMPLETED, false); + if (!firstAuthCompleted) { + LbryAnalytics.logEvent(LbryAnalytics.EVENT_FIRST_USER_AUTH); + sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_FIRST_AUTH_COMPLETED, true).apply(); + } findViewById(R.id.welcome_wait_container).setVisibility(View.GONE); findViewById(R.id.welcome_display).setVisibility(View.VISIBLE); @@ -96,10 +106,10 @@ public class FirstRunActivity extends AppCompatActivity { private void finishFirstRun() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_FIRST_RUN_COMPLETED, true).apply(); + sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_FIRST_RUN_COMPLETED, true).apply(); // first_run_completed event - + LbryAnalytics.logEvent(LbryAnalytics.EVENT_FIRST_RUN_COMPLETED); finish(); } diff --git a/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java b/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java new file mode 100644 index 00000000..42420958 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java @@ -0,0 +1,160 @@ +package io.lbry.browser; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; +import androidx.preference.PreferenceManager; + +import android.util.Log; + +import com.google.firebase.analytics.FirebaseAnalytics; +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import io.lbry.lbrysdk.LbrynetService; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class LbrynetMessagingService extends FirebaseMessagingService { + + private static final String TAG = "LbrynetMessagingService"; + private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL"; + private static final String TYPE_SUBSCRIPTION = "subscription"; + private static final String TYPE_REWARD = "reward"; + private static final String TYPE_INTERESTS = "interests"; + private static final String TYPE_CREATOR = "creator"; + private FirebaseAnalytics firebaseAnalytics; + + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + if (firebaseAnalytics == null) { + firebaseAnalytics = FirebaseAnalytics.getInstance(this); + } + + Map payload = remoteMessage.getData(); + if (payload != null) { + String type = payload.get("type"); + String url = payload.get("target"); + String title = payload.get("title"); + String body = payload.get("body"); + String name = payload.get("name"); // notification name + String contentTitle = payload.get("content_title"); + String channelUrl = payload.get("channel_url"); + //String publishTime = payload.get("publish_time"); + String publishTime = null; + + if (type != null && getEnabledTypes().indexOf(type) > -1 && body != null && body.trim().length() > 0) { + // only log the receive event for valid notifications received + if (firebaseAnalytics != null) { + Bundle bundle = new Bundle(); + bundle.putString("name", name); + firebaseAnalytics.logEvent("lbry_notification_receive", bundle); + } + + sendNotification(title, body, type, url, name, contentTitle, channelUrl, publishTime); + } + } + } + + @Override + public void onNewToken(String token) { + Log.d(TAG, "Refreshed token: " + token); + + // If you want to send messages to this application instance or + // manage this apps subscriptions on the server side, send the + // Instance ID token to your app server. + sendRegistrationToServer(token); + } + + /** + * Persist token to third-party servers. + * + * Modify this method to associate the user's FCM InstanceID token with any server-side account + * maintained by your application. + * + * @param token The new token. + */ + private void sendRegistrationToServer(String token) { + // TODO: Implement this method to send token to your app server. + } + + /** + * Create and show a simple notification containing the received FCM message. + * + * @param messageBody FCM message body received. + */ + private void sendNotification(String title, String messageBody, String type, String url, String name, + String contentTitle, String channelUrl, String publishTime) { + //Intent intent = new Intent(this, MainActivity.class); + //intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + if (url == null) { + if (TYPE_REWARD.equals(type)) { + url = "lbry://?rewards"; + } else { + // default to home page + url = "lbry://?discover"; + } + } + + Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + launchIntent.putExtra("notification_name", name); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, launchIntent, PendingIntent.FLAG_ONE_SHOT); + + Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + NotificationCompat.Builder notificationBuilder = + new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) + .setColor(ContextCompat.getColor(this, R.color.lbryGreen)) + .setSmallIcon(R.drawable.ic_lbry) + .setContentTitle(title) + .setContentText(messageBody) + .setAutoCancel(true) + .setSound(defaultSoundUri) + .setContentIntent(pendingIntent); + + NotificationManager notificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + // Since android Oreo notification channel is needed. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + NotificationChannel channel = new NotificationChannel( + NOTIFICATION_CHANNEL_ID, "LBRY Engagement", NotificationManager.IMPORTANCE_DEFAULT); + notificationManager.createNotificationChannel(channel); + } + + notificationManager.notify(3, notificationBuilder.build()); + } + + public List getEnabledTypes() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + List enabledTypes = new ArrayList(); + + if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS, true)) { + enabledTypes.add(TYPE_SUBSCRIPTION); + } + if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_REWARDS, true)) { + enabledTypes.add(TYPE_REWARD); + } + if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_CONTENT_INTERESTS, true)) { + enabledTypes.add(TYPE_INTERESTS); + } + if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_CREATOR, true)) { + enabledTypes.add(TYPE_CREATOR); + } + + return enabledTypes; + } +} \ No newline at end of file diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index 2e40a17a..9aa2f454 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -34,7 +34,11 @@ import android.widget.Toast; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ui.PlayerView; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; import com.google.android.material.snackbar.Snackbar; +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.InstanceIdResult; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -119,6 +123,7 @@ import io.lbry.browser.ui.wallet.RewardsFragment; import io.lbry.browser.ui.wallet.WalletFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.Lbryio; import io.lbry.lbrysdk.LbrynetService; @@ -136,6 +141,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static boolean startingSignInFlowActivity = false; public static boolean mainActive = false; private boolean enteringPIPMode = false; + private String firebaseMessagingToken; private Map openNavFragments; private static final Map fragmentClassNavIdMap = new HashMap<>(); @@ -181,6 +187,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static final String PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS = "io.lbry.browser.preference.notifications.Subscriptions"; public static final String PREFERENCE_KEY_NOTIFICATION_REWARDS = "io.lbry.browser.preference.notifications.Rewards"; public static final String PREFERENCE_KEY_NOTIFICATION_CONTENT_INTERESTS = "io.lbry.browser.preference.notifications.ContentInterests"; + public static final String PREFERENCE_KEY_NOTIFICATION_CREATOR = "io.lbry.browser.preference.notifications.Creator"; public static final String PREFERENCE_KEY_KEEP_SDK_BACKGROUND = "io.lbry.browser.preference.other.KeepSdkInBackground"; public static final String PREFERENCE_KEY_PARTICIPATE_DATA_NETWORK = "io.lbry.browser.preference.other.ParticipateInDataNetwork"; @@ -190,9 +197,11 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static final String PREFERENCE_KEY_INTERNAL_WALLET_RECEIVE_ADDRESS = "io.lbry.browser.preference.internal.WalletReceiveAddress"; public static final String PREFERENCE_KEY_INTERNAL_REWARDS_NOT_INTERESTED = "io.lbry.browser.preference.internal.RewardsNotInterested"; + public static final String PREFERENCE_KEY_INTERNAL_FIRST_RUN_COMPLETED = "io.lbry.browser.preference.internal.FirstRunCompleted"; + public static final String PREFERENCE_KEY_INTERNAL_FIRST_AUTH_COMPLETED = "io.lbry.browser.preference.internal.FirstAuthCompleted"; + private final int CHECK_SDK_READY_INTERVAL = 1000; - public static final String PREFERENCE_KEY_FIRST_RUN_COMPLETED = "io.lbry.browser.Preference.FirstRunCompleted"; public static final String PREFERENCE_KEY_AUTH_TOKEN = "io.lbry.browser.Preference.AuthToken"; public static final String SECURE_VALUE_KEY_SAVED_PASSWORD = "io.lbry.browser.PX"; @@ -273,6 +282,20 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } + LbryAnalytics.init(this); + FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(Task task) { + if (!task.isSuccessful()) { + return; + } + + // Get new Instance ID token + firebaseMessagingToken = task.getResult().getToken(); + android.util.Log.d("#HELP", firebaseMessagingToken); + } + }); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); @@ -504,7 +527,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static void openFileClaim(Claim claim, Context context) { Intent intent = new Intent(context, FileViewActivity.class); intent.putExtra("claimId", claim.getClaimId()); - intent.putExtra("url", claim.getPermanentUrl()); + intent.putExtra("url", !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl()); startingFileViewActivity = true; context.startActivity(intent); } @@ -935,7 +958,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener private void checkFirstRun() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - boolean firstRunCompleted = sp.getBoolean(PREFERENCE_KEY_FIRST_RUN_COMPLETED, false); + boolean firstRunCompleted = sp.getBoolean(PREFERENCE_KEY_INTERNAL_FIRST_RUN_COMPLETED, false); if (!firstRunCompleted) { startActivity(new Intent(this, FirstRunActivity.class)); } else if (!appStarted) { @@ -1004,6 +1027,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public void hideFloatingWalletBalance() { findViewById(R.id.floating_balance_main_container).setVisibility(View.GONE); } + public void hideFloatingRewardsValue() { + findViewById(R.id.floating_reward_container).setVisibility(View.INVISIBLE); + } private void initFloatingWalletBalance() { findViewById(R.id.floating_balance_container).setOnClickListener(new View.OnClickListener() { @@ -1455,6 +1481,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener hideActionBar(); lockDrawer(); findViewById(R.id.splash_view).setVisibility(View.VISIBLE); + LbryAnalytics.setCurrentScreen(MainActivity.this, "Splash", "Splash"); } protected Boolean doInBackground(Void... params) { BufferedReader reader = null; @@ -1547,6 +1574,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener fetchRewards(); checkUrlIntent(getIntent()); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_APP_LAUNCH); appStarted = true; } }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -1576,6 +1604,12 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } public void showFloatingUnclaimedRewards() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + boolean notInterestedInRewards = sp.getBoolean(PREFERENCE_KEY_INTERNAL_REWARDS_NOT_INTERESTED, false); + if (notInterestedInRewards) { + return; + } + ((TextView) findViewById(R.id.floating_reward_value)).setText(Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount)); findViewById(R.id.floating_reward_container).setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/io/lbry/browser/VerificationActivity.java b/app/src/main/java/io/lbry/browser/VerificationActivity.java index af6013aa..f616ffce 100644 --- a/app/src/main/java/io/lbry/browser/VerificationActivity.java +++ b/app/src/main/java/io/lbry/browser/VerificationActivity.java @@ -18,6 +18,7 @@ import io.lbry.browser.listener.SignInListener; import io.lbry.browser.listener.WalletSyncListener; import io.lbry.browser.model.lbryinc.User; import io.lbry.browser.tasks.lbryinc.FetchCurrentUserTask; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.Lbryio; public class VerificationActivity extends FragmentActivity implements SignInListener, WalletSyncListener { @@ -71,6 +72,7 @@ public class VerificationActivity extends FragmentActivity implements SignInList public void onResume() { super.onResume(); + LbryAnalytics.setCurrentScreen(this, "Verification", "Verification"); checkFlow(); } @@ -123,6 +125,10 @@ public class VerificationActivity extends FragmentActivity implements SignInList public void onEmailAdded(String email) { this.email = email; findViewById(R.id.verification_close_button).setVisibility(View.GONE); + + Bundle bundle = new Bundle(); + bundle.putString("email", email); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_EMAIL_ADDED, bundle); } public void onEmailEdit() { findViewById(R.id.verification_close_button).setVisibility(View.VISIBLE); @@ -131,6 +137,10 @@ public class VerificationActivity extends FragmentActivity implements SignInList Snackbar.make(findViewById(R.id.verification_pager), R.string.sign_in_successful, Snackbar.LENGTH_LONG).show(); sendBroadcast(new Intent(MainActivity.ACTION_USER_SIGN_IN_SUCCESS)); + Bundle bundle = new Bundle(); + bundle.putString("email", email); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_EMAIL_VERIFIED, bundle); + if (flow == VERIFICATION_FLOW_SIGN_IN) { final Intent resultIntent = new Intent(); resultIntent.putExtra("flow", VERIFICATION_FLOW_SIGN_IN); @@ -206,6 +216,8 @@ public class VerificationActivity extends FragmentActivity implements SignInList Lbryio.currentUser = user; if (user.isIdentityVerified() && user.isRewardApproved()) { // verified for rewards + LbryAnalytics.logEvent(LbryAnalytics.EVENT_REWARD_ELIGIBILITY_COMPLETED); + setResult(RESULT_OK); finish(); return; diff --git a/app/src/main/java/io/lbry/browser/model/Claim.java b/app/src/main/java/io/lbry/browser/model/Claim.java index cab80fdc..123aa04a 100644 --- a/app/src/main/java/io/lbry/browser/model/Claim.java +++ b/app/src/main/java/io/lbry/browser/model/Claim.java @@ -249,6 +249,7 @@ public class Claim { try { claim.setClaimId(searchResultObject.getString("claimId")); claim.setName(searchResultObject.getString("name")); + claim.setConfirmations(1); if (claim.getName().startsWith("@")) { claimUri.setChannelClaimId(claim.getClaimId()); diff --git a/app/src/main/java/io/lbry/browser/tasks/content/ChannelCreateUpdateTask.java b/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java similarity index 97% rename from app/src/main/java/io/lbry/browser/tasks/content/ChannelCreateUpdateTask.java rename to app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java index 3bbd4340..8b9a2ea2 100644 --- a/app/src/main/java/io/lbry/browser/tasks/content/ChannelCreateUpdateTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/ChannelCreateUpdateTask.java @@ -1,4 +1,4 @@ -package io.lbry.browser.tasks.content; +package io.lbry.browser.tasks; import android.os.AsyncTask; import android.view.View; @@ -14,7 +14,6 @@ import java.util.Map; import io.lbry.browser.exceptions.ApiCallException; import io.lbry.browser.model.Claim; -import io.lbry.browser.tasks.GenericTaskHandler; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; diff --git a/app/src/main/java/io/lbry/browser/tasks/content/ClaimResultHandler.java b/app/src/main/java/io/lbry/browser/tasks/ClaimResultHandler.java similarity index 81% rename from app/src/main/java/io/lbry/browser/tasks/content/ClaimResultHandler.java rename to app/src/main/java/io/lbry/browser/tasks/ClaimResultHandler.java index 9716686f..deb05445 100644 --- a/app/src/main/java/io/lbry/browser/tasks/content/ClaimResultHandler.java +++ b/app/src/main/java/io/lbry/browser/tasks/ClaimResultHandler.java @@ -1,4 +1,4 @@ -package io.lbry.browser.tasks.content; +package io.lbry.browser.tasks; import io.lbry.browser.model.Claim; diff --git a/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchStatCountTask.java b/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchStatCountTask.java new file mode 100644 index 00000000..1f8bb465 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchStatCountTask.java @@ -0,0 +1,73 @@ +package io.lbry.browser.tasks.lbryinc; + +import android.os.AsyncTask; +import android.view.View; + +import org.json.JSONArray; +import org.json.JSONException; + +import io.lbry.browser.exceptions.LbryioRequestException; +import io.lbry.browser.exceptions.LbryioResponseException; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbryio; + +public class FetchStatCountTask extends AsyncTask { + public static final int STAT_VIEW_COUNT = 1; + public static final int STAT_SUB_COUNT = 2; + + private String claimId; + private int stat; + private FetchStatCountHandler handler; + private View progressView; + private Exception error; + + public FetchStatCountTask(int stat, String claimId, View progressView, FetchStatCountHandler handler) { + this.stat = stat; + this.claimId = claimId; + this.progressView = progressView; + this.handler = handler; + } + + protected void onPreExecute() { + Helper.setViewVisibility(progressView, View.VISIBLE); + } + + protected Integer doInBackground(Void... params) { + int count = -1; + try { + if (stat != STAT_VIEW_COUNT && stat != STAT_SUB_COUNT) { + throw new LbryioRequestException("Invalid stat count specified."); + } + + JSONArray results = (JSONArray) + Lbryio.parseResponse(Lbryio.call( + stat == STAT_VIEW_COUNT ? "file" : "subscription", + stat == STAT_VIEW_COUNT ? "view_count" : "sub_count", + Lbryio.buildSingleParam("claim_id", claimId), + Helper.METHOD_GET, null)); + if (results.length() > 0) { + count = results.getInt(0); + } + } catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException ex) { + error = ex; + } + + return count; + } + + protected void onPostExecute(Integer count) { + Helper.setViewVisibility(progressView, View.GONE); + if (handler != null) { + if (count > -1) { + handler.onSuccess(count); + } else { + handler.onError(error); + } + } + } + + public interface FetchStatCountHandler { + void onSuccess(int count); + void onError(Exception error); + } +} diff --git a/app/src/main/java/io/lbry/browser/tasks/lbryinc/LogFileViewTask.java b/app/src/main/java/io/lbry/browser/tasks/lbryinc/LogFileViewTask.java new file mode 100644 index 00000000..c0055a7c --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/lbryinc/LogFileViewTask.java @@ -0,0 +1,55 @@ +package io.lbry.browser.tasks.lbryinc; + +import android.os.AsyncTask; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +import io.lbry.browser.exceptions.LbryioRequestException; +import io.lbry.browser.exceptions.LbryioResponseException; +import io.lbry.browser.model.Claim; +import io.lbry.browser.tasks.GenericTaskHandler; +import io.lbry.browser.utils.Lbryio; +import okhttp3.Response; + +public class LogFileViewTask extends AsyncTask { + private String uri; + private Claim claim; + private Exception error; + private GenericTaskHandler handler; + private long timeToStart; + + public LogFileViewTask(String uri, Claim claim, long timeToStart, GenericTaskHandler handler) { + this.uri = uri; + this.claim = claim; + this.timeToStart = timeToStart; + this.handler = handler; + } + protected Boolean doInBackground(Void... params) { + try { + Map options = new HashMap<>(); + options.put("uri", uri); + options.put("claim_id", claim.getClaimId()); + options.put("outpoint", String.format("%s:%d", claim.getTxid(), claim.getNout())); + if (timeToStart > 0) { + options.put("time_to_start", String.valueOf(timeToStart)); + } + Lbryio.call("file", "view", options, null).close(); + } catch (LbryioRequestException | LbryioResponseException ex) { + error = ex; + return false; + } + return true; + } + protected void onPostExecute(Boolean result) { + if (handler != null) { + if (result) { + handler.onSuccess(); + } else { + handler.onError(error); + } + } + } +} diff --git a/app/src/main/java/io/lbry/browser/tasks/content/LogPublishTask.java b/app/src/main/java/io/lbry/browser/tasks/lbryinc/LogPublishTask.java similarity index 96% rename from app/src/main/java/io/lbry/browser/tasks/content/LogPublishTask.java rename to app/src/main/java/io/lbry/browser/tasks/lbryinc/LogPublishTask.java index 31bc6915..f807f360 100644 --- a/app/src/main/java/io/lbry/browser/tasks/content/LogPublishTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/lbryinc/LogPublishTask.java @@ -1,4 +1,4 @@ -package io.lbry.browser.tasks.content; +package io.lbry.browser.tasks.lbryinc; import android.os.AsyncTask; diff --git a/app/src/main/java/io/lbry/browser/ui/allcontent/AllContentFragment.java b/app/src/main/java/io/lbry/browser/ui/allcontent/AllContentFragment.java index d30c4b9c..64b9fd5b 100644 --- a/app/src/main/java/io/lbry/browser/ui/allcontent/AllContentFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/allcontent/AllContentFragment.java @@ -38,6 +38,7 @@ import io.lbry.browser.tasks.FollowUnfollowTagTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.Predefined; import lombok.Getter; @@ -295,6 +296,10 @@ public class AllContentFragment extends BaseFragment implements SharedPreference fetchClaimSearchContent(true); } + Bundle bundle = new Bundle(); + bundle.putString("tag", tag.getLowercaseName()); + LbryAnalytics.logEvent(unfollowing ? LbryAnalytics.EVENT_TAG_UNFOLLOW : LbryAnalytics.EVENT_TAG_FOLLOW, bundle); + Context context = getContext(); if (context instanceof MainActivity) { ((MainActivity) context).saveSharedUserState(); @@ -359,6 +364,18 @@ public class AllContentFragment extends BaseFragment implements SharedPreference public void onResume() { super.onResume(); Helper.setWunderbarValue(null, getContext()); + checkParams(false); + + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + if (singleTagView) { + LbryAnalytics.setCurrentScreen(activity, "Tag", "Tag"); + } else { + LbryAnalytics.setCurrentScreen(activity, "All Content", "AllContent"); + } + } + PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); updateContentFromLinkText(); updateContentScopeLinkText(); @@ -424,7 +441,8 @@ public class AllContentFragment extends BaseFragment implements SharedPreference @Override public void onClaimClicked(Claim claim) { String claimId = claim.getClaimId(); - String url = claim.getPermanentUrl(); + String url = !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl(); + if (claim.getName().startsWith("@")) { // channel claim Context context = getContext(); diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java index 6f7a3ae0..542ee2e6 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelContentFragment.java @@ -251,7 +251,7 @@ public class ChannelContentFragment extends Fragment implements SharedPreference @Override public void onClaimClicked(Claim claim) { String claimId = claim.getClaimId(); - String url = claim.getPermanentUrl(); + String url = !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl(); if (claim.getName().startsWith("@")) { // channel claim Context context = getContext(); diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java index 6999fdc0..d546a1ec 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFormFragment.java @@ -42,15 +42,15 @@ import io.lbry.browser.model.Claim; import io.lbry.browser.model.NavMenuItem; import io.lbry.browser.model.Tag; import io.lbry.browser.model.WalletBalance; -import io.lbry.browser.tasks.GenericTaskHandler; import io.lbry.browser.tasks.UpdateSuggestedTagsTask; import io.lbry.browser.tasks.UploadImageTask; -import io.lbry.browser.tasks.content.ChannelCreateUpdateTask; -import io.lbry.browser.tasks.content.ClaimResultHandler; -import io.lbry.browser.tasks.content.LogPublishTask; +import io.lbry.browser.tasks.ChannelCreateUpdateTask; +import io.lbry.browser.tasks.ClaimResultHandler; +import io.lbry.browser.tasks.lbryinc.LogPublishTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import lombok.Getter; @@ -508,6 +508,7 @@ public class ChannelFormFragment extends BaseFragment implements WalletBalanceLi Context context = getContext(); if (context instanceof MainActivity) { MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Channel Form", "ChannelForm"); if (editMode) { ActionBar actionBar = activity.getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFragment.java index 7c8e4023..73f17a57 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelFragment.java @@ -25,6 +25,7 @@ import com.google.android.material.tabs.TabLayoutMediator; import java.math.BigDecimal; import java.text.DecimalFormat; +import java.text.NumberFormat; import java.util.List; import java.util.Map; @@ -38,11 +39,13 @@ import io.lbry.browser.model.lbryinc.Subscription; import io.lbry.browser.tasks.lbryinc.ChannelSubscribeTask; import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ResolveTask; +import io.lbry.browser.tasks.lbryinc.FetchStatCountTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.ui.controls.SolidIconView; import io.lbry.browser.ui.following.FollowingFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.Lbryio; import lombok.SneakyThrows; @@ -68,12 +71,18 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen private View buttonShare; private View buttonTip; private View buttonFollowUnfollow; + private int subCount; private SolidIconView iconFollowUnfollow; + private View layoutNothingAtLocation; + private View layoutLoadingState; + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_channel, container, false); + layoutLoadingState = root.findViewById(R.id.channel_view_loading_state); + layoutNothingAtLocation = root.findViewById(R.id.container_nothing_at_location); layoutDisplayArea = root.findViewById(R.id.channel_view_claim_display_area); layoutResolving = root.findViewById(R.id.channel_view_loading_container); @@ -244,12 +253,13 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen public void onResume() { super.onResume(); Context context = getContext(); - Map params = getParams(); String url = params != null && params.containsKey("url") ? (String) params.get("url") : null; Helper.setWunderbarValue(url, context); if (context instanceof MainActivity) { - ((MainActivity) context).addFetchChannelsListener(this); + MainActivity activity = (MainActivity) context; + activity.addFetchChannelsListener(this); + LbryAnalytics.setCurrentScreen(activity, "Channel", "Channel"); } checkParams(); @@ -284,6 +294,7 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen } } if (updateRequired) { + resetSubCount(); if (!Helper.isNullOrEmpty(url)) { resolveUrl(); } else if (claim == null) { @@ -302,7 +313,7 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen ResolveTask task = new ResolveTask(url, Lbry.LBRY_TV_CONNECTION_STRING, layoutResolving, new ClaimListResultHandler() { @Override public void onSuccess(List claims) { - if (claims.size() > 0) { + if (claims.size() > 0 && !Helper.isNullOrEmpty(claims.get(0).getClaimId())) { claim = claims.get(0); renderClaim(); checkOwnChannel(); @@ -320,7 +331,9 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen } private void renderNothingAtLocation() { - + layoutLoadingState.setVisibility(View.VISIBLE); + layoutNothingAtLocation.setVisibility(View.VISIBLE); + layoutDisplayArea.setVisibility(View.INVISIBLE); } public void setParams(Map params) { @@ -336,7 +349,9 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen return; } + loadSubCount(); checkIsFollowing(); + layoutLoadingState.setVisibility(View.GONE); layoutDisplayArea.setVisibility(View.VISIBLE); String thumbnailUrl = claim.getThumbnailUrl(); @@ -376,6 +391,36 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen }).attach(); } + private void resetSubCount() { + subCount = -1; + Helper.setViewText(textFollowerCount, null); + Helper.setViewVisibility(textFollowerCount, View.INVISIBLE); + } + + private void loadSubCount() { + if (claim != null) { + FetchStatCountTask task = new FetchStatCountTask( + FetchStatCountTask.STAT_SUB_COUNT, claim.getClaimId(), null, new FetchStatCountTask.FetchStatCountHandler() { + @Override + public void onSuccess(int count) { + try { + String displayText = getResources().getQuantityString(R.plurals.follower_count, count, NumberFormat.getInstance().format(count)); + Helper.setViewText(textFollowerCount, displayText); + Helper.setViewVisibility(textFollowerCount, View.VISIBLE); + } catch (IllegalStateException ex) { + // pass + } + } + + @Override + public void onError(Exception error) { + // pass + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + private static class ChannelPagerAdapter extends FragmentStateAdapter { private Claim channelClaim; public ChannelPagerAdapter(Claim channelClaim, FragmentActivity activity) { diff --git a/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java b/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java index fb4e173f..0d0207eb 100644 --- a/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/channel/ChannelManagerFragment.java @@ -40,6 +40,7 @@ import io.lbry.browser.tasks.ClaimListTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; public class ChannelManagerFragment extends BaseFragment implements ActionMode.Callback, SelectionModeListener, SdkStatusListener { @@ -109,7 +110,9 @@ public class ChannelManagerFragment extends BaseFragment implements ActionMode.C super.onResume(); Context context = getContext(); if (context instanceof MainActivity) { - ((MainActivity) context).setWunderbarValue(null); + MainActivity activity = (MainActivity) context; + activity.setWunderbarValue(null); + LbryAnalytics.setCurrentScreen(activity, "Channel Manager", "ChannelManager"); } if (!Lbry.SDK_READY) { diff --git a/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java b/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java index ca9b15d4..8cfa9b35 100644 --- a/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/editorschoice/EditorsChoiceFragment.java @@ -34,6 +34,7 @@ import io.lbry.browser.tasks.ClaimSearchTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.Predefined; public class EditorsChoiceFragment extends BaseFragment { @@ -86,7 +87,13 @@ public class EditorsChoiceFragment extends BaseFragment { public void onResume() { super.onResume(); - Helper.setWunderbarValue(null, getContext()); + Context context = getContext(); + Helper.setWunderbarValue(null, context); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Editor's Choice", "EditorsChoice"); + } + if (contentListAdapter == null || contentListAdapter.getItemCount() == 0) { fetchClaimSearchContent(); } else { diff --git a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java index 98e262f3..8a9b8fd3 100644 --- a/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/following/FollowingFragment.java @@ -46,6 +46,7 @@ import io.lbry.browser.listener.ChannelItemSelectionListener; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.Lbryio; import io.lbry.browser.utils.Predefined; @@ -327,8 +328,13 @@ public class FollowingFragment extends BaseFragment implements public void onResume() { super.onResume(); - Helper.setWunderbarValue(null, getContext()); - PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + Context context = getContext(); + Helper.setWunderbarValue(null, context); + PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Subscriptions", "Subscriptions"); + } // check if subscriptions exist if (suggestedChannelAdapter != null) { @@ -562,7 +568,7 @@ public class FollowingFragment extends BaseFragment implements @Override public void onClaimClicked(Claim claim) { String claimId = claim.getClaimId(); - String url = claim.getPermanentUrl(); + String url = !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl(); if (claim.getName().startsWith("@")) { // channel claim diff --git a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java index e51172d1..208e8b9c 100644 --- a/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/search/SearchFragment.java @@ -1,5 +1,6 @@ package io.lbry.browser.ui.search; +import android.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; @@ -28,6 +29,7 @@ import io.lbry.browser.tasks.ResolveTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import lombok.Setter; @@ -87,9 +89,15 @@ public class SearchFragment extends BaseFragment implements public void onResume() { super.onResume(); - Helper.setWunderbarValue(currentQuery, getContext()); - PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + Context context = getContext(); + Helper.setWunderbarValue(currentQuery, context); + PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Search", "Search"); + } if (!Helper.isNullOrEmpty(currentQuery)) { + logSearch(currentQuery); search(currentQuery, currentFrom); } else { noQueryView.setVisibility(View.VISIBLE); @@ -170,17 +178,28 @@ public class SearchFragment extends BaseFragment implements unresolved.setValue(resolved.getValue()); unresolved.setSigningChannel(resolved.getSigningChannel()); unresolved.setUnresolved(false); + unresolved.setConfirmations(resolved.getConfirmations()); resultListAdapter.notifyDataSetChanged(); } } + private void logSearch(String query) { + Bundle bundle = new Bundle(); + bundle.putString("query", query); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_SEARCH, bundle); + } + public void search(String query, int from) { boolean queryChanged = checkQuery(query); if (!queryChanged && from > 0) { currentFrom = from; } + if (queryChanged) { + logSearch(query); + } + searchLoading = true; SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); boolean canShowMatureContent = sp.getBoolean(MainActivity.PREFERENCE_KEY_SHOW_MATURE_CONTENT, false); diff --git a/app/src/main/java/io/lbry/browser/ui/settings/SettingsFragment.java b/app/src/main/java/io/lbry/browser/ui/settings/SettingsFragment.java index 73104e0a..65a7377c 100644 --- a/app/src/main/java/io/lbry/browser/ui/settings/SettingsFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/settings/SettingsFragment.java @@ -11,6 +11,7 @@ import androidx.preference.PreferenceManager; import io.lbry.browser.MainActivity; import io.lbry.browser.R; +import io.lbry.browser.utils.LbryAnalytics; public class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { @Override @@ -38,7 +39,12 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Shared @Override public void onResume() { super.onResume(); - PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + Context context = getContext(); + PreferenceManager.getDefaultSharedPreferences(context).registerOnSharedPreferenceChangeListener(this); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Settings", "Settings"); + } } @Override public void onPause() { diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java index 6746964f..0d1a906b 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/InvitesFragment.java @@ -41,15 +41,16 @@ import io.lbry.browser.model.lbryinc.Invitee; import io.lbry.browser.tasks.ClaimListResultHandler; import io.lbry.browser.tasks.ClaimListTask; import io.lbry.browser.tasks.GenericTaskHandler; -import io.lbry.browser.tasks.content.ChannelCreateUpdateTask; -import io.lbry.browser.tasks.content.ClaimResultHandler; -import io.lbry.browser.tasks.content.LogPublishTask; +import io.lbry.browser.tasks.ChannelCreateUpdateTask; +import io.lbry.browser.tasks.ClaimResultHandler; +import io.lbry.browser.tasks.lbryinc.LogPublishTask; import io.lbry.browser.tasks.lbryinc.FetchInviteStatusTask; import io.lbry.browser.tasks.lbryinc.FetchReferralCodeTask; import io.lbry.browser.tasks.lbryinc.InviteByEmailTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.Lbryio; @@ -287,9 +288,14 @@ public class InvitesFragment extends BaseFragment implements SdkStatusListener, super.onResume(); layoutAccountDriver.setVisibility(Lbryio.isSignedIn() ? View.GONE : View.VISIBLE); + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Invites", "Invites"); + } + fetchInviteStatus(); if (!Lbry.SDK_READY) { - Context context = getContext(); if (context instanceof MainActivity) { MainActivity activity = (MainActivity) context; activity.addSdkStatusListener(this); diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java index b3aa0ec5..3851969e 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/RewardsFragment.java @@ -1,6 +1,7 @@ package io.lbry.browser.ui.wallet; import android.content.Context; +import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Typeface; import android.os.AsyncTask; @@ -13,6 +14,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -32,6 +34,7 @@ import io.lbry.browser.tasks.lbryinc.FetchRewardsTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.Lbryio; public class RewardsFragment extends BaseFragment implements RewardListAdapter.RewardClickListener, SdkStatusListener { @@ -87,8 +90,13 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R checkRewardsStatus(); fetchRewards(); + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Rewards", "Rewards"); + } + if (!Lbry.SDK_READY) { - Context context = getContext(); if (context instanceof MainActivity) { MainActivity activity = (MainActivity) context; activity.addSdkStatusListener(this); @@ -161,9 +169,17 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R linkNotInterested.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { + + Context context = getContext(); + if (context instanceof MainActivity) { - ((MainActivity) context).onBackPressed(); + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + sp.edit().putBoolean(MainActivity.PREFERENCE_KEY_INTERNAL_REWARDS_NOT_INTERESTED, true).apply(); + + MainActivity activity = (MainActivity) context; + activity.hideFloatingRewardsValue(); + activity.onBackPressed(); } } }); @@ -210,13 +226,17 @@ public class RewardsFragment extends BaseFragment implements RewardListAdapter.R } public void updateUnclaimedRewardsValue() { - String accountDriverTitle = getResources().getQuantityString( - R.plurals.available_credits, - Lbryio.totalUnclaimedRewardAmount == 1 ? 1 : 2, - Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount)); - double unclaimedRewardAmountUsd = Lbryio.totalUnclaimedRewardAmount * Lbryio.LBCUSDRate; - Helper.setViewText(textAccountDriverTitle, accountDriverTitle); - Helper.setViewText(textFreeCreditsWorth, getString(R.string.free_credits_worth, Helper.USD_CURRENCY_FORMAT.format(unclaimedRewardAmountUsd))); + try { + String accountDriverTitle = getResources().getQuantityString( + R.plurals.available_credits, + Lbryio.totalUnclaimedRewardAmount == 1 ? 1 : 2, + Helper.shortCurrencyFormat(Lbryio.totalUnclaimedRewardAmount)); + double unclaimedRewardAmountUsd = Lbryio.totalUnclaimedRewardAmount * Lbryio.LBCUSDRate; + Helper.setViewText(textAccountDriverTitle, accountDriverTitle); + Helper.setViewText(textFreeCreditsWorth, getString(R.string.free_credits_worth, Helper.USD_CURRENCY_FORMAT.format(unclaimedRewardAmountUsd))); + } catch (IllegalStateException ex) { + // pass + } } @Override diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/TransactionHistoryFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/TransactionHistoryFragment.java index 86ee9982..4076ddbb 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/TransactionHistoryFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/TransactionHistoryFragment.java @@ -23,6 +23,7 @@ import io.lbry.browser.model.Transaction; import io.lbry.browser.tasks.wallet.TransactionListTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; public class TransactionHistoryFragment extends BaseFragment implements TransactionListAdapter.TransactionClickListener { @@ -77,6 +78,12 @@ public class TransactionHistoryFragment extends BaseFragment implements Transact @Override public void onResume() { super.onResume(); + Context context = getContext(); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Transaction History", "TransactionHistory"); + } + if (adapter != null && adapter.getItemCount() > 0 && transactionList != null) { transactionList.setAdapter(adapter); } diff --git a/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java b/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java index 79e00c30..3475378b 100644 --- a/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/wallet/WalletFragment.java @@ -45,6 +45,7 @@ import io.lbry.browser.tasks.wallet.WalletSendTask; import io.lbry.browser.ui.BaseFragment; import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.Lbry; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.browser.utils.LbryUri; import io.lbry.browser.utils.Lbryio; @@ -393,9 +394,14 @@ public class WalletFragment extends BaseFragment implements SdkStatusListener, W public void onResume() { super.onResume(); - Helper.setWunderbarValue(null, getContext()); + Context context = getContext(); + Helper.setWunderbarValue(null, context); + if (context instanceof MainActivity) { + MainActivity activity = (MainActivity) context; + LbryAnalytics.setCurrentScreen(activity, "Wallet", "Wallet"); + } + if (!Lbry.SDK_READY) { - Context context = getContext(); if (context instanceof MainActivity) { MainActivity activity = (MainActivity) context; activity.addSdkStatusListener(this); diff --git a/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java b/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java new file mode 100644 index 00000000..ce20980c --- /dev/null +++ b/app/src/main/java/io/lbry/browser/utils/LbryAnalytics.java @@ -0,0 +1,53 @@ +package io.lbry.browser.utils; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; + +import com.google.firebase.analytics.FirebaseAnalytics; + +public class LbryAnalytics { + + public static final String EVENT_APP_EXCEPTION = "app_exception"; + public static final String EVENT_APP_LAUNCH = "app_launch"; + 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"; + public static final String EVENT_FIRST_USER_AUTH = "first_user_auth"; + public static final String EVENT_LBRY_NOTIFICATION_OPEN = "lbry_notification_open"; + public static final String EVENT_LBRY_NOTIFICATION_RECEIVE = "lbry_notification_receive"; + public static final String EVENT_OPEN_FILE_PAGE = "open_file_page"; + public static final String EVENT_PLAY = "play"; + public static final String EVENT_PURCHASE_URI = "purchase_uri"; + public static final String EVENT_REWARD_ELIGIBILITY_COMPLETED = "reward_eligibility_completed"; + public static final String EVENT_TAG_FOLLOW = "tag_follow"; + public static final String EVENT_TAG_UNFOLLOW = "tag_unfollow"; + public static final String EVENT_PUBLISH = "publish"; + public static final String EVENT_CHANNEL_CREATE = "channel_create"; + public static final String EVENT_SEARCH = "search"; + + private static FirebaseAnalytics analytics; + + public static void init(Context context) { + analytics = FirebaseAnalytics.getInstance(context); + } + + public static void setCurrentScreen(Activity activity, String name, String className) { + analytics.setCurrentScreen(activity, name, className); + } + + public static void logEvent(String name) { + logEvent(name, null); + } + + public static void logEvent(String name, Bundle bundle) { + analytics.logEvent(name, bundle); + } + + public static void logException(String message, String exceptionName) { + Bundle bundle = new Bundle(); + bundle.putString("message", message); + bundle.putString("name", exceptionName); + logEvent("app_exception", bundle); + } +} diff --git a/app/src/main/res/drawable-anydpi/ic_fullscreen.xml b/app/src/main/res/drawable-anydpi/ic_fullscreen.xml new file mode 100644 index 00000000..b8f99968 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_fullscreen.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_fullscreen_exit.xml b/app/src/main/res/drawable-anydpi/ic_fullscreen_exit.xml new file mode 100644 index 00000000..f8116a2e --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_fullscreen_exit.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_fullscreen.png b/app/src/main/res/drawable-hdpi/ic_fullscreen.png new file mode 100644 index 00000000..10decc15 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fullscreen.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_fullscreen_exit.png b/app/src/main/res/drawable-hdpi/ic_fullscreen_exit.png new file mode 100644 index 00000000..a43ff782 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fullscreen.png b/app/src/main/res/drawable-mdpi/ic_fullscreen.png new file mode 100644 index 00000000..baed2aa2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fullscreen.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fullscreen_exit.png b/app/src/main/res/drawable-mdpi/ic_fullscreen_exit.png new file mode 100644 index 00000000..b4a402c2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fullscreen.png b/app/src/main/res/drawable-xhdpi/ic_fullscreen.png new file mode 100644 index 00000000..7f3b8676 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fullscreen.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fullscreen_exit.png b/app/src/main/res/drawable-xhdpi/ic_fullscreen_exit.png new file mode 100644 index 00000000..9c052625 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fullscreen_exit.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fullscreen.png b/app/src/main/res/drawable-xxhdpi/ic_fullscreen.png new file mode 100644 index 00000000..ff166dd0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fullscreen.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fullscreen_exit.png b/app/src/main/res/drawable-xxhdpi/ic_fullscreen_exit.png new file mode 100644 index 00000000..60c521bb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fullscreen_exit.png differ diff --git a/app/src/main/res/layout/activity_file_view.xml b/app/src/main/res/layout/activity_file_view.xml index f2f7109c..93619cd0 100644 --- a/app/src/main/res/layout/activity_file_view.xml +++ b/app/src/main/res/layout/activity_file_view.xml @@ -4,9 +4,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/pageBackground" - android:fitsSystemWindows="true"> + android:background="@color/pageBackground"> + android:visibility="invisible"> - + + + + + + + - + android:layout_height="match_parent" + app:controller_layout_id="@layout/exo_playback_control_view"/> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/exo_playback_control_view.xml b/app/src/main/res/layout/exo_playback_control_view.xml new file mode 100644 index 00000000..c3adccfb --- /dev/null +++ b/app/src/main/res/layout/exo_playback_control_view.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_channel.xml b/app/src/main/res/layout/fragment_channel.xml index 36979755..3fd3504d 100644 --- a/app/src/main/res/layout/fragment_channel.xml +++ b/app/src/main/res/layout/fragment_channel.xml @@ -30,6 +30,8 @@ android:textSize="16sp" android:textFontWeight="300" /> + + #CC000000 #F4E866 + #33000000 + @color/nextLbryGreen + @color/nextLbryGreen + @color/nextLbryGreenSemiTransparent + #EEEEEE #555555 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 71ea513c..d47f47c1 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -14,6 +14,11 @@ #CC000000 #F4E866 + #33000000 + @color/nextLbryGreen + @color/nextLbryGreen + @color/nextLbryGreenSemiTransparent + #222222 #AAAAAA #333333 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f0406d18..ec5bf9a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -59,15 +59,28 @@ Loading decentralized data... Related Content Share LBRY content - + View + Play + + %1$s view + %1$s views + Unsupported Content Sorry, we are unable to display this content in the app. You can find the file named %1$s in your downloads folder. + There\'s nothing at this location. + Publish something here + This content cannot be accessed at this time. Please try again later. + 00:00 There\'s nothing here yet.\nPlease check back later. Content Website reposted + + %1$s follower + %1$s followers + Content & User interface