url history and player fixes

This commit is contained in:
Akinwale Ariwodola 2020-05-14 19:25:16 +01:00
parent fb2b798106
commit 47f56950a5
32 changed files with 395 additions and 123 deletions

View file

@ -20,6 +20,9 @@
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider"/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_lbry" />
@ -39,7 +42,7 @@
android:supportsPictureInPicture="true"
android:theme="@style/AppTheme.NoActionBar"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan">
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View file

@ -1,7 +1,6 @@
package io.lbry.browser;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
@ -34,7 +33,6 @@ import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.NavUtils;
import androidx.core.content.ContextCompat;
import androidx.core.widget.NestedScrollView;
import androidx.preference.PreferenceManager;
@ -43,11 +41,15 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.github.chrisbanes.photoview.PhotoView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.cast.CastPlayer;
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
@ -84,6 +86,7 @@ import io.lbry.browser.model.ClaimCacheKey;
import io.lbry.browser.model.Fee;
import io.lbry.browser.model.LbryFile;
import io.lbry.browser.model.Tag;
import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.model.lbryinc.Reward;
import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.tasks.ReadTextFileTask;
@ -115,6 +118,8 @@ public class FileViewActivity extends AppCompatActivity {
private static final int RELATED_CONTENT_SIZE = 16;
private static boolean startingShareActivity;
private PlayerControlView castControlView;
private Player currentPlayer;
private boolean backStackLost;
private boolean loadingNewClaim;
private boolean stopServiceReceived;
@ -163,11 +168,8 @@ public class FileViewActivity extends AppCompatActivity {
instance = this;
ClaimCacheKey key = new ClaimCacheKey();
key.setClaimId(claimId);
if (url.contains("#")) {
key.setPermanentUrl(url); // use the same url for the key so that we can match the key for any value that's the same
key.setCanonicalUrl(url);
key.setShortUrl(url);
}
key.setUrl(url); // use the same url for the key so that we can match the key for any value that's the same
if (Lbry.claimCache.containsKey(key)) {
claim = Lbry.claimCache.get(key);
checkAndResetNowPlayingClaim();
@ -181,6 +183,7 @@ public class FileViewActivity extends AppCompatActivity {
currentUrl = url;
logUrlEvent(url);
Helper.saveUrlHistory(url, claim != null ? claim.getTitle() : null, UrlSuggestion.TYPE_FILE);
if (claim == null) {
MainActivity.clearNowPlayingClaim(this);
resolveUrl(url);
@ -218,6 +221,7 @@ public class FileViewActivity extends AppCompatActivity {
}
};
castControlView = findViewById(R.id.file_view_cast_control_view);
initUi();
onWalletBalanceUpdated();
renderClaim();
@ -278,9 +282,7 @@ public class FileViewActivity extends AppCompatActivity {
ClaimCacheKey key = new ClaimCacheKey();
key.setClaimId(newClaimId);
if (!Helper.isNullOrEmpty(newUrl) && newUrl.contains("#")) {
key.setPermanentUrl(newUrl);
key.setCanonicalUrl(newUrl);
key.setShortUrl(newUrl);
key.setUrl(newUrl);
}
loadClaimForCacheKey(key, newUrl);
} else if (!Helper.isNullOrEmpty(newUrl)) {
@ -290,9 +292,7 @@ public class FileViewActivity extends AppCompatActivity {
onNewClaim(newUrl);
ClaimCacheKey key = new ClaimCacheKey();
key.setPermanentUrl(newUrl);
key.setCanonicalUrl(newUrl);
key.setShortUrl(newUrl);
key.setUrl(newUrl);
loadClaimForCacheKey(key, newUrl);
}
}
@ -313,6 +313,7 @@ public class FileViewActivity extends AppCompatActivity {
private void loadClaimForCacheKey(ClaimCacheKey key, String url) {
if (Lbry.claimCache.containsKey(key)) {
claim = Lbry.claimCache.get(key);
Helper.saveUrlHistory(url, claim.getTitle(), UrlSuggestion.TYPE_FILE);
checkAndResetNowPlayingClaim();
if (claim.getFile() == null) {
loadFile();
@ -322,6 +323,7 @@ public class FileViewActivity extends AppCompatActivity {
}
renderClaim();
} else {
Helper.saveUrlHistory(url, null, UrlSuggestion.TYPE_FILE);
findViewById(R.id.file_view_claim_display_area).setVisibility(View.INVISIBLE);
MainActivity.clearNowPlayingClaim(this);
resolveUrl(url);
@ -452,6 +454,8 @@ public class FileViewActivity extends AppCompatActivity {
super.onResume();
MainActivity.mainActive = false;
MainActivity.startingFileViewActivity = false;
loadAndScheduleDurations();
if (Lbry.SDK_READY) {
initFloatingWalletBalance();
}
@ -468,12 +472,25 @@ public class FileViewActivity extends AppCompatActivity {
claim = claims.get(0);
if (Claim.TYPE_REPOST.equalsIgnoreCase(claim.getValueType())) {
claim = claim.getRepostedClaim();
// cache the reposted claim too for subsequent loads
ClaimCacheKey key = ClaimCacheKey.fromClaim(claim);
Lbry.claimCache.put(key, claim);
Lbry.addClaimToCache(claim);
if (claim.getName().startsWith("@")) {
// this is a reposted channel, so finish this activity and launch the channel url
Intent intent = new Intent(MainActivity.ACTION_OPEN_CHANNEL_URL);
intent.putExtra("url", !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl());
sendBroadcast(intent);
bringMainTaskToFront();
finish();
return;
}
} else {
Lbry.addClaimToCache(claim);
}
Helper.saveUrlHistory(url, claim.getTitle(), UrlSuggestion.TYPE_FILE);
// also save view history
checkAndResetNowPlayingClaim();
loadFile();
renderClaim();
@ -644,7 +661,14 @@ public class FileViewActivity extends AppCompatActivity {
}
});
findViewById(R.id.player_toggle_full_screen).setOnClickListener(new View.OnClickListener() {
findViewById(R.id.player_toggle_cast).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
toggleCast();
}
});
findViewById(R.id.player_toggle_fullscreen).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// check full screen mode
@ -664,7 +688,6 @@ public class FileViewActivity extends AppCompatActivity {
Intent intent = new Intent(MainActivity.ACTION_OPEN_CHANNEL_URL);
intent.putExtra("url", !Helper.isNullOrEmpty(publisher.getShortUrl()) ? publisher.getShortUrl() : publisher.getPermanentUrl());
sendBroadcast(intent);
bringMainTaskToFront();
finish();
}
@ -862,12 +885,12 @@ public class FileViewActivity extends AppCompatActivity {
boolean newPlayerCreated = false;
if (MainActivity.appPlayer == null) {
MainActivity.appPlayer = new SimpleExoPlayer.Builder(this).build();
MainActivity.castPlayer = new CastPlayer(MainActivity.castContext);
newPlayerCreated = true;
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
PlayerView view = findViewById(R.id.file_view_exoplayer_view);
view.setPlayer(MainActivity.appPlayer);
if (MainActivity.nowPlayingClaim != null &&
@ -881,6 +904,20 @@ public class FileViewActivity extends AppCompatActivity {
showBuffering();
MainActivity.appPlayer.addListener(fileViewPlayerListener);
MainActivity.castPlayer.addListener(fileViewPlayerListener);
MainActivity.castPlayer.setSessionAvailabilityListener(new SessionAvailabilityListener() {
@Override
public void onCastSessionAvailable() {
setCurrentPlayer(MainActivity.castPlayer);
}
@Override
public void onCastSessionUnavailable() {
setCurrentPlayer(MainActivity.appPlayer);
}
});
castControlView.setPlayer(MainActivity.castPlayer);
MainActivity.setNowPlayingClaim(claim, FileViewActivity.this);
String userAgent = Util.getUserAgent(this, getString(R.string.app_name));
@ -893,6 +930,47 @@ public class FileViewActivity extends AppCompatActivity {
MainActivity.appPlayer.prepare(mediaSource, true, true);
}
private void setCurrentPlayer(Player currentPlayer) {
if (this.currentPlayer == currentPlayer) {
return;
}
// View management.
if (currentPlayer == MainActivity.appPlayer) {
//localPlayerView.setVisibility(View.VISIBLE);
castControlView.hide();
((ImageView) findViewById(R.id.player_image_cast_toggle)).setImageResource(R.drawable.ic_cast);
} else /* currentPlayer == castPlayer */ {
castControlView.show();
((ImageView) findViewById(R.id.player_image_cast_toggle)).setImageResource(R.drawable.ic_cast_connected);
}
// Player state management.
long playbackPositionMs = C.TIME_UNSET;
int windowIndex = C.INDEX_UNSET;
boolean playWhenReady = false;
Player previousPlayer = this.currentPlayer;
if (previousPlayer != null) {
// Save state from the previous player.
int playbackState = previousPlayer.getPlaybackState();
if (playbackState != Player.STATE_ENDED) {
playbackPositionMs = previousPlayer.getCurrentPosition();
playWhenReady = previousPlayer.getPlayWhenReady();
}
previousPlayer.stop(true);
}
this.currentPlayer = currentPlayer;
// Media queue management.
/*if (currentPlayer == exoPlayer) {
exoPlayer.prepare(concatenatingMediaSource);
}*/
currentPlayer.seekTo(playbackPositionMs);
currentPlayer.setPlayWhenReady(true);
}
private void resetViewCount() {
TextView textViewCount = findViewById(R.id.file_view_view_count);
Helper.setViewText(textViewCount, null);
@ -1179,7 +1257,7 @@ public class FileViewActivity extends AppCompatActivity {
if (claim.getName().startsWith("@")) {
// opening a channel
Intent intent = new Intent(MainActivity.ACTION_OPEN_CHANNEL_URL);
intent.putExtra("url", !Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl());
intent.putExtra("url", claim.getPermanentUrl());
sendBroadcast(intent);
bringMainTaskToFront();
finish();
@ -1409,6 +1487,18 @@ public class FileViewActivity extends AppCompatActivity {
Helper.setViewText(findViewById(R.id.player_duration_total), Helper.formatDuration(Double.valueOf(totalDuration / 1000.0).longValue()));
}
private void loadAndScheduleDurations() {
if (MainActivity.appPlayer != null) {
if (totalDuration == 0) {
elapsedDuration = MainActivity.appPlayer.getCurrentPosition();
totalDuration = MainActivity.appPlayer.getDuration();
}
renderElapsedDuration();
renderTotalDuration();
scheduleElapsedPlayback();
}
}
private void logPlay(String url, long startTimeMillis) {
long timeToStartMillis = startTimeMillis > 0 ? System.currentTimeMillis() - startTimeMillis : 0;
@ -1554,6 +1644,19 @@ public class FileViewActivity extends AppCompatActivity {
findViewById(R.id.floating_balance_main_container).setVisibility(View.VISIBLE);
}
private void toggleCast() {
if (!MainActivity.castPlayer.isCastSessionAvailable()) {
showError(getString(R.string.no_cast_session_available));
return;
}
if (currentPlayer == MainActivity.appPlayer) {
setCurrentPlayer(MainActivity.castPlayer);
} else {
setCurrentPlayer(MainActivity.appPlayer);
}
}
private void onDownloadAborted() {
downloadInProgress = false;
@ -1598,6 +1701,7 @@ public class FileViewActivity extends AppCompatActivity {
final Set<String> categories = baseIntent.getCategories();
if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
task.moveToFront();
finish();
return;
}
}

View file

@ -36,7 +36,9 @@ import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ext.cast.CastPlayer;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.material.snackbar.Snackbar;
@ -105,6 +107,7 @@ import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
import io.lbry.browser.tasks.MergeSubscriptionsTask;
import io.lbry.browser.tasks.claim.ResolveTask;
import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask;
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
import io.lbry.browser.tasks.wallet.LoadSharedUserStateTask;
import io.lbry.browser.tasks.wallet.SaveSharedUserStateTask;
@ -140,6 +143,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
private Map<String, Class> specialRouteFragmentClassMap;
private boolean inPictureInPictureMode;
public static SimpleExoPlayer appPlayer;
public static CastContext castContext;
public static CastPlayer castPlayer;
public static Claim nowPlayingClaim;
public static boolean startingFilePickerActivity = false;
public static boolean startingShareActivity = false;
@ -225,7 +230,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
private NavigationMenuAdapter navMenuAdapter;
private UrlSuggestionListAdapter urlSuggestionListAdapter;
private List<UrlSuggestion> recentHistory;
private List<UrlSuggestion> recentUrlHistory;
private boolean hasLoadedFirstBalance;
// broadcast receivers
@ -295,7 +300,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
initKeyStore();
loadAuthToken();
dbHelper = new DatabaseHelper(this);
if (!isDarkMode()) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
@ -315,6 +319,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
});
super.onCreate(savedInstanceState);
castContext = CastContext.getSharedInstance(this);
dbHelper = new DatabaseHelper(this);
checkNotificationOpenIntent(getIntent());
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
@ -542,9 +548,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
private Claim getCachedClaimForUrl(String url) {
ClaimCacheKey key = new ClaimCacheKey();
key.setCanonicalUrl(url);
key.setPermanentUrl(url);
key.setShortUrl(url);
key.setUrl(url);
return Lbry.claimCache.containsKey(key) ? Lbry.claimCache.get(key) : null;
}
@ -881,19 +885,25 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
for (int i = 0; i < claims.size(); i++) {
// build a simple url from the claim for matching
Claim claim = claims.get(i);
Claim actualClaim = claim;
boolean isRepost = false;
if (Claim.TYPE_REPOST.equalsIgnoreCase(claim.getValueType())) {
actualClaim = claim.getRepostedClaim();
isRepost = true;
}
if (Helper.isNullOrEmpty(claim.getName())) {
continue;
}
LbryUri simpleUrl = new LbryUri();
if (claim.getName().startsWith("@")) {
if (actualClaim.getName().startsWith("@") && !isRepost) {
// channel
simpleUrl.setChannelName(claim.getName());
simpleUrl.setChannelName(actualClaim.getName());
} else {
simpleUrl.setStreamName(claim.getName());
}
urlSuggestionListAdapter.setClaimForUrl(simpleUrl, claim);
urlSuggestionListAdapter.setClaimForUrl(simpleUrl, actualClaim);
}
urlSuggestionListAdapter.notifyDataSetChanged();
}
@ -909,10 +919,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
private void displayUrlSuggestionsForNoInput() {
urlSuggestionListAdapter.clear();
List<UrlSuggestion> blankSuggestions = buildDefaultSuggestionsForBlankUrl();
urlSuggestionListAdapter.addUrlSuggestions(blankSuggestions);
List<String> urls = urlSuggestionListAdapter.getItemUrls();
resolveUrlSuggestions(urls);
loadDefaultSuggestionsForBlankUrl();
}
private void handleUriInputChanged(String text) {
@ -948,25 +955,48 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private List<UrlSuggestion> buildDefaultSuggestionsForBlankUrl() {
List<UrlSuggestion> suggestions = new ArrayList<>();
if (recentHistory != null && recentHistory.size() > 0) {
// show recent history if avaiable
suggestions = new ArrayList<>(recentHistory);
} else {
try {
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_FILE, "What is LBRY?", LbryUri.parse("lbry://what#19b9c243bea0c45175e6a6027911abbad53e983e")));
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_CHANNEL, "LBRYCast", LbryUri.parse("lbry://@lbrycast#4c29f8b013adea4d5cca1861fb2161d5089613ea")));
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_CHANNEL, "The LBRY Channel", LbryUri.parse("lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a")));
private void loadDefaultSuggestionsForBlankUrl() {
if (recentUrlHistory != null && recentUrlHistory.size() > 0) {
urlSuggestionListAdapter.addUrlSuggestions(recentUrlHistory);
}
FetchRecentUrlHistoryTask task = new FetchRecentUrlHistoryTask(DatabaseHelper.getInstance(), new FetchRecentUrlHistoryTask.FetchRecentUrlHistoryHandler() {
@Override
public void onSuccess(List<UrlSuggestion> recentHistory) {
List<UrlSuggestion> suggestions = new ArrayList<>(recentHistory);
List<UrlSuggestion> lbrySuggestions = buildLbryUrlSuggestions();
if (suggestions.size() < 10) {
for (int i = suggestions.size(), j = 0; i < 10 && j < lbrySuggestions.size(); i++, j++) {
suggestions.add(lbrySuggestions.get(j));
}
} else if (suggestions.size() == 0) {
suggestions.addAll(lbrySuggestions);
}
for (UrlSuggestion suggestion : suggestions) {
suggestion.setUseTextAsDescription(true);
}
} catch (LbryUriException ex) {
// pass
recentUrlHistory = new ArrayList<>(suggestions);
urlSuggestionListAdapter.clear();
urlSuggestionListAdapter.addUrlSuggestions(recentUrlHistory);
List<String> urls = urlSuggestionListAdapter.getItemUrls();
resolveUrlSuggestions(urls);
}
});
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private List<UrlSuggestion> buildLbryUrlSuggestions() {
List<UrlSuggestion> suggestions = new ArrayList<>();
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_FILE, "What is LBRY?", LbryUri.tryParse("lbry://what#19b9c243bea0c45175e6a6027911abbad53e983e")));
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_CHANNEL, "LBRYCast", LbryUri.tryParse("lbry://@lbrycast#4c29f8b013adea4d5cca1861fb2161d5089613ea")));
suggestions.add(new UrlSuggestion(
UrlSuggestion.TYPE_CHANNEL, "The LBRY Channel", LbryUri.tryParse("lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a")));
for (UrlSuggestion suggestion : suggestions) {
suggestion.setUseTextAsDescription(true);
}
return suggestions;
}
@ -975,7 +1005,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
List<UrlSuggestion> suggestions = new ArrayList<UrlSuggestion>();
if (LbryUri.PROTO_DEFAULT.equalsIgnoreCase(text)) {
return buildDefaultSuggestionsForBlankUrl();
loadDefaultSuggestionsForBlankUrl();
return recentUrlHistory != null ? recentUrlHistory : new ArrayList<>();
}
// First item is always search

View file

@ -16,6 +16,7 @@ import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.ui.controls.SolidIconView;
import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri;
import lombok.Setter;
@ -96,7 +97,7 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
iconStringId = R.string.fa_at;
fullTitle = item.getTitle();
desc = item.getClaim() != null ? item.getClaim().getTitle() :
(item.isUseTextAsDescription() ? item.getText() : String.format(context.getString(R.string.view_channel_url_desc), item.getText()));
((item.isUseTextAsDescription() && !Helper.isNullOrEmpty(item.getText())) ? item.getText() : String.format(context.getString(R.string.view_channel_url_desc), item.getText()));
break;
case UrlSuggestion.TYPE_TAG:
iconStringId = R.string.fa_hashtag;
@ -113,7 +114,7 @@ public class UrlSuggestionListAdapter extends RecyclerView.Adapter<UrlSuggestion
iconStringId = R.string.fa_file;
fullTitle = item.getTitle();
desc = item.getClaim() != null ? item.getClaim().getTitle() :
(item.isUseTextAsDescription() ? item.getText() : String.format(context.getString(R.string.view_file_url_desc), item.getText()));
((item.isUseTextAsDescription() && !Helper.isNullOrEmpty(item.getText())) ? item.getText() : String.format(context.getString(R.string.view_file_url_desc), item.getText()));
break;
}

View file

@ -21,6 +21,7 @@ import io.lbry.browser.utils.LbryUri;
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "LbryApp.db";
private static DatabaseHelper instance;
private static final String[] SQL_CREATE_TABLES = {
// local subscription store
@ -58,7 +59,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?";
private static final String SQL_GET_SUBSCRIPTIONS = "SELECT channel_name, url FROM subscriptions";
private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?)";
private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)";
private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history";
private static final String SQL_CLEAR_URL_HISTORY_BEFORE_TIME = "DELETE FROM url_history WHERE timestamp < ?";
private static final String SQL_GET_RECENT_URL_HISTORY = "SELECT value, url, type FROM url_history ORDER BY timestamp DESC LIMIT 10";
@ -77,6 +78,10 @@ public class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, String.format("%s/%s", context.getFilesDir().getAbsolutePath(), DATABASE_NAME), null, DATABASE_VERSION);
instance = this;
}
public static DatabaseHelper getInstance() {
return instance;
}
public void onCreate(SQLiteDatabase db) {
for (String sql : SQL_CREATE_TABLES) {
@ -113,13 +118,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
while (cursor.moveToNext()) {
UrlSuggestion suggestion = new UrlSuggestion();
suggestion.setText(cursor.getString(0));
suggestion.setUri(cursor.isNull(1) ? null : LbryUri.tryParse(cursor.getString(1)));
suggestion.setType(cursor.getInt(2));
try {
suggestion.setUri(cursor.isNull(1) ? null : LbryUri.parse(cursor.getString(1)));
} catch (LbryUriException ex) {
// don't fail if the LbryUri is invalid
}
suggestion.setTitleUrlOnly(true);
suggestions.add(suggestion);
}
} finally {

View file

@ -372,9 +372,10 @@ public class Claim {
Claim signingChannel = new Claim();
signingChannel.setClaimId(searchResultObject.getString("channel_claim_id"));
signingChannel.setName(searchResultObject.getString("channel"));
claimUri.setChannelClaimId(signingChannel.getClaimId());
claimUri.setChannelName(signingChannel.getName());
LbryUri channelUri = new LbryUri();
channelUri.setChannelClaimId(signingChannel.getClaimId());
channelUri.setChannelName(signingChannel.getName());
signingChannel.setPermanentUrl(channelUri.toString());
claim.setSigningChannel(signingChannel);
}

View file

@ -5,34 +5,36 @@ import androidx.annotation.Nullable;
import io.lbry.browser.utils.Helper;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* Class to represent a key to check equality with another object
*/
@ToString
public class ClaimCacheKey {
@Getter
@Setter
private String claimId;
@Getter
@Setter
private String canonicalUrl;
@Getter
@Setter
private String permanentUrl;
@Getter
@Setter
private String shortUrl;
@Getter
@Setter
private String vanityUrl;
private String url;
public static ClaimCacheKey fromClaimShortUrl(Claim claim) {
ClaimCacheKey key = new ClaimCacheKey();
key.setUrl(claim.getShortUrl());
return key;
}
public static ClaimCacheKey fromClaimPermanentUrl(Claim claim) {
ClaimCacheKey key = new ClaimCacheKey();
key.setUrl(claim.getPermanentUrl());
return key;
}
public static ClaimCacheKey fromClaim(Claim claim) {
ClaimCacheKey key = new ClaimCacheKey();
key.setClaimId(claim.getClaimId());
key.setCanonicalUrl(claim.getCanonicalUrl());
key.setPermanentUrl(claim.getPermanentUrl());
key.setShortUrl(claim.getShortUrl());
key.setUrl(!Helper.isNullOrEmpty(claim.getShortUrl()) ? claim.getShortUrl() : claim.getPermanentUrl());
return key;
}
@ -41,44 +43,24 @@ public class ClaimCacheKey {
if (obj == null || !(obj instanceof ClaimCacheKey)) {
return false;
}
ClaimCacheKey key = (ClaimCacheKey) obj;
if (!Helper.isNullOrEmpty(claimId) && !Helper.isNullOrEmpty(key.getClaimId())) {
return claimId.equalsIgnoreCase(key.getClaimId());
}
if (!Helper.isNullOrEmpty(permanentUrl) && !Helper.isNullOrEmpty(key.getPermanentUrl())) {
return permanentUrl.equalsIgnoreCase(key.getPermanentUrl());
if (!Helper.isNullOrEmpty(url) && !Helper.isNullOrEmpty(key.getUrl())) {
return url.equalsIgnoreCase(key.getUrl());
}
if (!Helper.isNullOrEmpty(canonicalUrl) && !Helper.isNullOrEmpty(key.getCanonicalUrl())) {
return canonicalUrl.equalsIgnoreCase(key.getCanonicalUrl());
}
if (!Helper.isNullOrEmpty(shortUrl) && !Helper.isNullOrEmpty(key.getShortUrl())) {
return shortUrl.equalsIgnoreCase(key.getShortUrl());
}
if (!Helper.isNullOrEmpty(vanityUrl) && !Helper.isNullOrEmpty(key.getVanityUrl())) {
return vanityUrl.equalsIgnoreCase(key.getVanityUrl());
}
return false;
}
@Override
public int hashCode() {
if (!Helper.isNullOrEmpty(url)) {
return url.hashCode();
}
if (!Helper.isNullOrEmpty(claimId)) {
return claimId.hashCode();
}
if (!Helper.isNullOrEmpty(permanentUrl)) {
return permanentUrl.hashCode();
}
if (!Helper.isNullOrEmpty(canonicalUrl)) {
return canonicalUrl.hashCode();
}
if (!Helper.isNullOrEmpty(shortUrl)) {
return shortUrl.hashCode();
}
if (!Helper.isNullOrEmpty(vanityUrl)) {
return vanityUrl.hashCode();
}
return super.hashCode();
}

View file

@ -15,6 +15,7 @@ public class UrlSuggestion {
private LbryUri uri;
private Claim claim; // associated claim if resolved
private boolean titleTextOnly;
private boolean titleUrlOnly;
private boolean useTextAsDescription;
public UrlSuggestion() {
@ -34,6 +35,10 @@ public class UrlSuggestion {
}
public String getTitle() {
if (titleUrlOnly && (type == TYPE_CHANNEL || type == TYPE_FILE)) {
return uri.toString();
}
if (!titleTextOnly) {
switch (type) {
case TYPE_CHANNEL:

View file

@ -30,7 +30,7 @@ public class ClaimSearchTask extends AsyncTask<Void, Void, List<Claim>> {
}
protected List<Claim> doInBackground(Void... params) {
try {
return Lbry.claimSearch(options, connectionString);
return Helper.filterInvalidReposts(Lbry.claimSearch(options, connectionString));
} catch (ApiCallException ex) {
error = ex;
return null;

View file

@ -34,7 +34,7 @@ public class ResolveTask extends AsyncTask<Void, Void, List<Claim>> {
}
protected List<Claim> doInBackground(Void... params) {
try {
return Lbry.resolve(urls, connectionString);
return Helper.filterInvalidReposts(Lbry.resolve(urls, connectionString));
} catch (ApiCallException ex) {
error = ex;
return null;

View file

@ -0,0 +1,37 @@
package io.lbry.browser.tasks.localdata;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.AsyncTask;
import java.util.ArrayList;
import java.util.List;
import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.model.UrlSuggestion;
public class FetchRecentUrlHistoryTask extends AsyncTask<Void, Void, List<UrlSuggestion>> {
private DatabaseHelper dbHelper;
private FetchRecentUrlHistoryHandler handler;
public FetchRecentUrlHistoryTask(DatabaseHelper dbHelper, FetchRecentUrlHistoryHandler handler) {
this.dbHelper = dbHelper;
this.handler = handler;
}
protected List<UrlSuggestion> doInBackground(Void... params) {
try {
SQLiteDatabase db = dbHelper.getReadableDatabase();
return DatabaseHelper.getRecentHistory(db);
} catch (SQLiteException ex) {
return new ArrayList<>();
}
}
protected void onPostExecute(List<UrlSuggestion> recentHistory) {
if (handler != null) {
handler.onSuccess(recentHistory);
}
}
public interface FetchRecentUrlHistoryHandler {
void onSuccess(List<UrlSuggestion> recentHistory);
}
}

View file

@ -9,21 +9,21 @@ import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.tasks.GenericTaskHandler;
public class CreateUrlHistoryTask extends AsyncTask<Void, Void, Boolean> {
private Context context;
public class SaveUrlHistoryTask extends AsyncTask<Void, Void, Boolean> {
private DatabaseHelper dbHelper;
private UrlSuggestion suggestion;
private GenericTaskHandler handler;
private SaveUrlHistoryHandler handler;
private Exception error;
public CreateUrlHistoryTask(UrlSuggestion suggestion, Context context, GenericTaskHandler handler) {
public SaveUrlHistoryTask(UrlSuggestion suggestion, DatabaseHelper dbHelper, SaveUrlHistoryHandler handler) {
this.suggestion = suggestion;
this.context = context;
this.dbHelper = dbHelper;
this.handler = handler;
}
protected Boolean doInBackground(Void... params) {
try {
SQLiteDatabase db = ((MainActivity) context).getDbHelper().getWritableDatabase();
SQLiteDatabase db = dbHelper.getWritableDatabase();
DatabaseHelper.createOrUpdateUrlHistoryItem(suggestion.getText(), suggestion.getUri().toString(), suggestion.getType(), db);
} catch (Exception ex) {
error = ex;
@ -35,10 +35,15 @@ public class CreateUrlHistoryTask extends AsyncTask<Void, Void, Boolean> {
protected void onPostExecute(Boolean result) {
if (handler != null) {
if (result) {
handler.onSuccess();
handler.onSuccess(suggestion);
} else {
handler.onError(error);
}
}
}
public interface SaveUrlHistoryHandler {
void onSuccess(UrlSuggestion item);
void onError(Exception error);
}
}

View file

@ -0,0 +1,4 @@
package io.lbry.browser.tasks.localdata;
public class SaveViewHistoryTask {
}

View file

@ -31,10 +31,13 @@ import java.util.Map;
import io.lbry.browser.MainActivity;
import io.lbry.browser.R;
import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.dialog.SendTipDialogFragment;
import io.lbry.browser.exceptions.LbryUriException;
import io.lbry.browser.listener.FetchChannelsListener;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.ClaimCacheKey;
import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.model.lbryinc.Subscription;
import io.lbry.browser.tasks.lbryinc.ChannelSubscribeTask;
import io.lbry.browser.tasks.claim.ClaimListResultHandler;
@ -296,13 +299,24 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
if (updateRequired) {
resetSubCount();
if (!Helper.isNullOrEmpty(url)) {
resolveUrl();
// check if the claim is already cached
ClaimCacheKey key = new ClaimCacheKey();
key.setUrl(url);
if (Lbry.claimCache.containsKey(key)) {
claim = Lbry.claimCache.get(key);
} else {
resolveUrl();
}
} else if (claim == null) {
// nothing at this location
renderNothingAtLocation();
}
}
if (!Helper.isNullOrEmpty(url)) {
Helper.saveUrlHistory(url, claim != null ? claim.getTitle() : null, UrlSuggestion.TYPE_CHANNEL);
}
if (claim != null) {
renderClaim();
}
@ -315,6 +329,10 @@ public class ChannelFragment extends BaseFragment implements FetchChannelsListen
public void onSuccess(List<Claim> claims) {
if (claims.size() > 0 && !Helper.isNullOrEmpty(claims.get(0).getClaimId())) {
claim = claims.get(0);
if (!Helper.isNullOrEmpty(url)) {
Helper.saveUrlHistory(url, claim.getTitle(), UrlSuggestion.TYPE_CHANNEL);
}
renderClaim();
checkOwnChannel();
} else {

View file

@ -141,7 +141,7 @@ public class SearchFragment extends BaseFragment implements
private void resolveFeaturedItem(String vanityUrl) {
final ClaimCacheKey key = new ClaimCacheKey();
key.setVanityUrl(vanityUrl);
key.setUrl(vanityUrl);
if (Lbry.claimCache.containsKey(key)) {
Claim cachedClaim = Lbry.claimCache.get(key);
updateFeaturedItemFromResolvedClaim(cachedClaim);

View file

@ -12,6 +12,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
@ -40,10 +41,13 @@ import java.util.Map;
import java.util.Random;
import io.lbry.browser.MainActivity;
import io.lbry.browser.data.DatabaseHelper;
import io.lbry.browser.dialog.ContentFromDialogFragment;
import io.lbry.browser.dialog.ContentSortDialogFragment;
import io.lbry.browser.model.Claim;
import io.lbry.browser.model.Tag;
import io.lbry.browser.model.UrlSuggestion;
import io.lbry.browser.tasks.localdata.SaveUrlHistoryTask;
import okhttp3.MediaType;
public final class Helper {
@ -555,4 +559,26 @@ public final class Helper {
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(HtmlCompat.fromHtml(textView.getText().toString(), HtmlCompat.FROM_HTML_MODE_LEGACY));
}
public static List<Claim> filterInvalidReposts(List<Claim> claims) {
List<Claim> filtered = new ArrayList<>();
for (Claim claim : claims) {
if (Claim.TYPE_REPOST.equalsIgnoreCase(claim.getValueType()) && claim.getRepostedClaim() == null) {
continue;
}
filtered.add(claim);
}
return filtered;
}
public static void saveUrlHistory(String url, String title, int type) {
DatabaseHelper dbHelper = DatabaseHelper.getInstance();
if (dbHelper != null) {
UrlSuggestion suggestion = new UrlSuggestion();
suggestion.setUri(LbryUri.tryParse(url));
suggestion.setType(type);
suggestion.setText(Helper.isNull(title) ? "" : title);
new SaveUrlHistoryTask(suggestion, dbHelper, null).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
}

View file

@ -250,9 +250,7 @@ public final class Lbry {
Claim claim = Claim.fromJSONObject(result.getJSONObject(keys.next()));
claims.add(claim);
// TODO: Add/remove/prune entries claim cache management
ClaimCacheKey key = ClaimCacheKey.fromClaim(claim);
claimCache.put(key, claim);
addClaimToCache(claim);
}
}
} catch (LbryRequestException | LbryResponseException | JSONException ex) {
@ -396,8 +394,7 @@ public final class Lbry {
Claim claim = Claim.fromJSONObject(items.getJSONObject(i));
claims.add(claim);
ClaimCacheKey key = ClaimCacheKey.fromClaim(claim);
claimCache.put(key, claim);
addClaimToCache(claim);
}
}
@ -452,4 +449,15 @@ public final class Lbry {
}
}
}
public static void addClaimToCache(Claim claim) {
ClaimCacheKey fullKey = ClaimCacheKey.fromClaim(claim);
ClaimCacheKey shortUrlKey = ClaimCacheKey.fromClaimShortUrl(claim);
ClaimCacheKey permanentUrlKey = ClaimCacheKey.fromClaimPermanentUrl(claim);
claimCache.put(fullKey, claim);
claimCache.put(permanentUrlKey, claim);
if (!Helper.isNullOrEmpty(shortUrlKey.getUrl())) {
claimCache.put(shortUrlKey, claim);
}
}
}

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="#FF000000"
android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF"
android:alpha="0.8">
<path
android:fillColor="#FF000000"
android:pathData="M1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM19,7L5,7v1.63c3.96,1.28 7.09,4.41 8.37,8.37L19,17L19,7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11zM21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

View file

@ -162,9 +162,15 @@
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
<com.google.android.exoplayer2.ui.PlayerControlView android:id="@+id/file_view_cast_control_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:repeat_toggle_modes="all|one"
app:show_timeout="-1"/>
<androidx.core.widget.NestedScrollView
android:id="@+id/file_view_scroll_view"
android:layout_width="match_parent"

View file

@ -58,7 +58,6 @@
</LinearLayout>
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />

View file

@ -20,9 +20,9 @@
android:id="@+id/url_suggestions_container"
android:background="@color/pageBackground"
android:elevation="6dp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/url_suggestions"
android:layout_width="match_parent"

View file

@ -21,7 +21,25 @@
style="@style/ExoMediaButton.Pause" />
<RelativeLayout
android:id="@+id/player_toggle_full_screen"
android:id="@+id/player_toggle_cast"
android:clickable="true"
android:background="?attr/selectableItemBackground"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:visibility="gone">
<ImageView
android:id="@+id/player_image_cast_toggle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_cast"
android:tint="@color/white" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/player_toggle_fullscreen"
android:clickable="true"
android:background="?attr/selectableItemBackground"
android:layout_width="48dp"
@ -41,7 +59,7 @@
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/player_toggle_full_screen"
android:layout_toLeftOf="@id/player_toggle_fullscreen"
android:paddingLeft="16dp"
android:paddingRight="4dp">
<TextView

View file

@ -73,6 +73,7 @@
<string name="delete_file">Delete file</string>
<string name="confirm_delete_file_message">Are you sure you want to remove this file from your device?</string>
<string name="unable_to_view_url">Failed to load %1$s. Please try again later.</string>
<string name="no_cast_session_available">There is no cast session available at this time.</string>
<plurals name="view_count">
<item quantity="one">%1$s view</item>
<item quantity="other">%1$s views</item>