diff --git a/app/build.gradle b/app/build.gradle index 4140e140..766f0b2e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -79,6 +79,7 @@ dependencies { implementation 'com.hbb20:ccp:2.3.8' implementation 'com.github.chrisbanes:PhotoView:2.3.0' + implementation 'com.atlassian.commonmark:commonmark:0.14.0' compileOnly 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10' diff --git a/app/src/main/java/io/lbry/browser/FileViewActivity.java b/app/src/main/java/io/lbry/browser/FileViewActivity.java index fa3d8232..1889e41c 100644 --- a/app/src/main/java/io/lbry/browser/FileViewActivity.java +++ b/app/src/main/java/io/lbry/browser/FileViewActivity.java @@ -20,6 +20,10 @@ import android.text.format.DateUtils; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.webkit.WebResourceRequest; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; @@ -48,6 +52,9 @@ import com.google.android.flexbox.FlexboxLayoutManager; import com.google.android.material.button.MaterialButton; import com.google.android.material.snackbar.Snackbar; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; import org.json.JSONException; import org.json.JSONObject; @@ -56,6 +63,7 @@ import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -74,6 +82,7 @@ import io.lbry.browser.model.LbryFile; import io.lbry.browser.model.Tag; import io.lbry.browser.model.lbryinc.Reward; import io.lbry.browser.model.lbryinc.Subscription; +import io.lbry.browser.tasks.ReadTextFileTask; import io.lbry.browser.tasks.claim.ClaimListResultHandler; import io.lbry.browser.tasks.claim.ClaimSearchTask; import io.lbry.browser.tasks.file.DeleteFileTask; @@ -161,6 +170,7 @@ public class FileViewActivity extends AppCompatActivity { loadFile(); } } + setContentView(R.layout.activity_file_view); checkIsFileComplete(); @@ -218,6 +228,14 @@ public class FileViewActivity extends AppCompatActivity { walletBalanceListeners.remove(listener); } + private void initWebView() { + WebView webView = findViewById(R.id.file_view_webview); + webView.setWebViewClient(new LbryWebViewClient(this)); + WebSettings webSettings = webView.getSettings(); + webSettings.setAllowFileAccess(true); + webSettings.setJavaScriptEnabled(true); + } + private void logUrlEvent(String url) { Bundle bundle = new Bundle(); bundle.putString("uri", url); @@ -466,6 +484,8 @@ public class FileViewActivity extends AppCompatActivity { } private void initUi() { + initWebView(); + findViewById(R.id.file_view_title_area).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -788,10 +808,10 @@ public class FileViewActivity extends AppCompatActivity { 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))); } - - } + findViewById(R.id.file_view_icon_follow_unfollow).setVisibility(claim.getSigningChannel() != null ? View.VISIBLE : View.GONE); + MaterialButton mainActionButton = findViewById(R.id.file_view_main_action_button); if (claim.isPlayable()) { mainActionButton.setText(R.string.play); @@ -801,8 +821,10 @@ public class FileViewActivity extends AppCompatActivity { mainActionButton.setText(R.string.download); } - if (claim.isFree() && (claim.isPlayable() || claim.isViewable())) { - onMainActionButtonClicked(); + if (claim.isFree()) { + if (claim.isPlayable() || (claim.isViewable() && Lbry.SDK_READY)) { + onMainActionButtonClicked(); + } } loadRelatedContent(); @@ -1008,32 +1030,39 @@ public class FileViewActivity extends AppCompatActivity { handled = true; } else if (claim.isViewable()) { // check type and display - if (mediaType.startsWith("image")) { - // display the image - View container = findViewById(R.id.file_view_imageviewer_container); - PhotoView photoView = findViewById(R.id.file_view_imageviewer); - - boolean fileExists = false; - LbryFile claimFile = claim.getFile(); - if (claimFile != null && !Helper.isNullOrEmpty(claimFile.getDownloadPath())) { - File file = new File(claimFile.getDownloadPath()); - fileExists = file.exists(); - - if (fileExists) { - Uri fileUri = Uri.fromFile(file); - Glide.with(getApplicationContext()).load(fileUri).centerInside().into(photoView); - hideFloatingWalletBalance(); - container.setVisibility(View.VISIBLE); - } - } - - if (!fileExists) { - showError(getString(R.string.claim_file_not_found, claimFile != null ? claimFile.getDownloadPath() : "")); - } - } else if (mediaType.startsWith("text")) { - // show browser (and parse markdown too) + boolean fileExists = false; + LbryFile claimFile = claim.getFile(); + Uri fileUri = null; + if (claimFile != null && !Helper.isNullOrEmpty(claimFile.getDownloadPath())) { + File file = new File(claimFile.getDownloadPath()); + fileUri = Uri.fromFile(file); + fileExists = file.exists(); + } + if (!fileExists) { + showError(getString(R.string.claim_file_not_found, claimFile != null ? claimFile.getDownloadPath() : "")); + } else if (fileUri != null) { + if (mediaType.startsWith("image")) { + // display the image + View container = findViewById(R.id.file_view_imageviewer_container); + PhotoView photoView = findViewById(R.id.file_view_imageviewer); + + Glide.with(getApplicationContext()).load(fileUri).centerInside().into(photoView); + hideFloatingWalletBalance(); + container.setVisibility(View.VISIBLE); + } else if (mediaType.startsWith("text")) { + // show web view (and parse markdown too) + View container = findViewById(R.id.file_view_webview_container); + WebView webView = findViewById(R.id.file_view_webview); + if (Arrays.asList("text/markdown", "text/md").contains(mediaType.toLowerCase())) { + loadMarkdownFromFile(claimFile.getDownloadPath()); + } else { + webView.loadUrl(fileUri.toString()); + } + hideFloatingWalletBalance(); + container.setVisibility(View.VISIBLE); + } + handled = true; } - handled = true; } } @@ -1042,6 +1071,57 @@ public class FileViewActivity extends AppCompatActivity { } } + private void loadMarkdownFromFile(String filePath) { + ReadTextFileTask task = new ReadTextFileTask(filePath, new ReadTextFileTask.ReadTextFileHandler() { + @Override + public void onSuccess(String text) { + String html = buildMarkdownHtml(text); + WebView webView = findViewById(R.id.file_view_webview); + webView.loadData(html, "text/html", "utf-8"); + } + + @Override + public void onError(Exception error) { + showError(error.getMessage()); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private String buildMarkdownHtml(String markdown) { + Parser parser = Parser.builder().build(); + Node document = parser.parse(markdown); + HtmlRenderer renderer = HtmlRenderer.builder().build(); + String markdownHtml = renderer.render(document); + + return "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
\n" + + markdownHtml + + "
\n" + + " \n" + + " "; + } + public void showError(String message) { Snackbar.make(findViewById(R.id.file_view_claim_display_area), message, Snackbar.LENGTH_LONG). setTextColor(Color.WHITE). @@ -1113,6 +1193,14 @@ public class FileViewActivity extends AppCompatActivity { if (isImageViewerVisible()) { findViewById(R.id.file_view_imageviewer_container).setVisibility(View.GONE); + restoreMainActionButton(); + showFloatingWalletBalance(); + return; + } + if (isWebViewVisible()) { + findViewById(R.id.file_view_webview_container).setVisibility(View.GONE); + restoreMainActionButton(); + showFloatingWalletBalance(); return; } @@ -1127,14 +1215,17 @@ public class FileViewActivity extends AppCompatActivity { } private boolean isWebViewVisible() { - return false; + return findViewById(R.id.file_view_webview_container).getVisibility() == View.VISIBLE; } protected void onUserLeaveHint() { - if (stopServiceReceived) { + if (stopServiceReceived || + claim == null || + !claim.isPlayable()) { return; } + if (startingShareActivity) { // share activity triggered this, so reset the flag at this point new Handler().postDelayed(new Runnable() { @@ -1145,6 +1236,7 @@ public class FileViewActivity extends AppCompatActivity { }, 1000); return; } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !MainActivity.mainActive) { PictureInPictureParams params = new PictureInPictureParams.Builder().build(); enterPictureInPictureMode(params); @@ -1432,6 +1524,9 @@ public class FileViewActivity extends AppCompatActivity { private void hideFloatingWalletBalance() { findViewById(R.id.floating_balance_main_container).setVisibility(View.GONE); } + private void showFloatingWalletBalance() { + findViewById(R.id.floating_balance_main_container).setVisibility(View.VISIBLE); + } private void onDownloadAborted() { downloadInProgress = false; @@ -1451,4 +1546,20 @@ public class FileViewActivity extends AppCompatActivity { findViewById(R.id.file_view_main_action_loading).setVisibility(View.INVISIBLE); findViewById(R.id.file_view_main_action_button).setVisibility(View.VISIBLE); } + + private static class LbryWebViewClient extends WebViewClient { + private Context context; + public LbryWebViewClient(Context context) { + this.context = context; + } + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + Uri url = request.getUrl(); + if (context != null) { + Intent intent = new Intent(Intent.ACTION_VIEW, url); + context.startActivity(intent); + } + return true; + } + } } diff --git a/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java b/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java index 42420958..62536467 100644 --- a/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java +++ b/app/src/main/java/io/lbry/browser/LbrynetMessagingService.java @@ -20,6 +20,7 @@ import com.google.firebase.analytics.FirebaseAnalytics; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; +import io.lbry.browser.utils.LbryAnalytics; import io.lbry.lbrysdk.LbrynetService; import java.io.UnsupportedEncodingException; @@ -61,7 +62,7 @@ public class LbrynetMessagingService extends FirebaseMessagingService { if (firebaseAnalytics != null) { Bundle bundle = new Bundle(); bundle.putString("name", name); - firebaseAnalytics.logEvent("lbry_notification_receive", bundle); + firebaseAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_RECEIVE, bundle); } sendNotification(title, body, type, url, name, contentTitle, channelUrl, publishTime); diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index 654816c2..2d11bd13 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -315,6 +315,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener }); super.onCreate(savedInstanceState); + checkNotificationOpenIntent(getIntent()); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); @@ -441,6 +442,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener protected void onNewIntent(Intent intent) { super.onNewIntent(intent); checkUrlIntent(intent); + checkNotificationOpenIntent(intent); } public void addSdkStatusListener(SdkStatusListener listener) { @@ -1837,6 +1839,22 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener return false; } + private void checkNotificationOpenIntent(Intent intent) { + if (intent != null) { + String notificationName = intent.getStringExtra("notification_name"); + if (notificationName != null) { + logNotificationOpen(notificationName); + } + } + } + + private void logNotificationOpen(String name) { + Bundle bundle = new Bundle(); + bundle.putString("name", name); + LbryAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_OPEN, bundle); + } + + private void registerServiceActionsReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(LbrynetService.LBRY_SDK_SERVICE_STARTED); diff --git a/app/src/main/java/io/lbry/browser/tasks/ReadTextFileTask.java b/app/src/main/java/io/lbry/browser/tasks/ReadTextFileTask.java new file mode 100644 index 00000000..3dfa6b93 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/ReadTextFileTask.java @@ -0,0 +1,55 @@ +package io.lbry.browser.tasks; + +import android.os.AsyncTask; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.Buffer; + +import io.lbry.browser.utils.Helper; + +public class ReadTextFileTask extends AsyncTask { + private String filePath; + private Exception error; + private ReadTextFileHandler handler; + public ReadTextFileTask(String filePath, ReadTextFileHandler handler) { + this.filePath = filePath; + this.handler = handler; + } + protected String doInBackground(Void... params) { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + } catch (IOException ex) { + error = ex; + return null; + } finally { + Helper.closeCloseable(reader); + } + + return sb.toString(); + } + protected void onPostExecute(String text) { + if (handler != null) { + if (!Helper.isNull(text)) { + handler.onSuccess(text); + } else { + handler.onError(error); + } + } + } + + public interface ReadTextFileHandler { + void onSuccess(String text); + void onError(Exception error); + } +} diff --git a/app/src/main/res/layout/activity_file_view.xml b/app/src/main/res/layout/activity_file_view.xml index 29bfa6fd..61767607 100644 --- a/app/src/main/res/layout/activity_file_view.xml +++ b/app/src/main/res/layout/activity_file_view.xml @@ -624,6 +624,17 @@ android:layout_height="match_parent" /> + + + + \ No newline at end of file