Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit d2f07b0

Browse files
keianhzobluemarvin
authored andcommitted
History feed (#1656)
* History feed * Do not show redirects in the history feed Also avoid storing URLs manually and rely o the history delegate for all history management * Refactor title bar update code * Added support for getVisited and visited links colouring Also fixed an issue with history sections and made the historyListener a delegate as having multiple listeners is not really necessary * Fixed rebase issue * Update history entries when revisited Also fixed history cleanup ranges * Handle dim when history is opened
1 parent f3ada54 commit d2f07b0

54 files changed

Lines changed: 2173 additions & 245 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/src/common/shared/org/mozilla/vrbrowser/browser/HistoryStore.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import android.os.Handler
1010
import android.os.Looper
1111
import kotlinx.coroutines.GlobalScope
1212
import kotlinx.coroutines.future.future
13+
import mozilla.components.concept.storage.PageObservation
14+
import mozilla.components.concept.storage.VisitInfo
1315
import mozilla.components.concept.storage.VisitType
1416
import org.mozilla.vrbrowser.VRBrowserApplication
1517
import java.util.concurrent.CompletableFuture
@@ -40,16 +42,53 @@ class HistoryStore constructor(val context: Context) {
4042
storage.getVisited()
4143
}
4244

43-
fun addHistory(aURL: String, visitType: VisitType) = GlobalScope.future {
45+
fun getDetailedHistory(): CompletableFuture<List<VisitInfo>?> = GlobalScope.future {
46+
storage.getDetailedVisits(0, excludeTypes = listOf(
47+
VisitType.NOT_A_VISIT,
48+
VisitType.REDIRECT_TEMPORARY,
49+
VisitType.REDIRECT_PERMANENT,
50+
VisitType.RELOAD))
51+
}
52+
53+
fun recordVisit(aURL: String, visitType: VisitType) = GlobalScope.future {
4454
storage.recordVisit(aURL, visitType)
4555
notifyListeners()
4656
}
4757

58+
fun recordObservation(aURL: String, observation: PageObservation) = GlobalScope.future {
59+
storage.recordObservation(aURL, observation)
60+
notifyListeners()
61+
}
62+
4863
fun deleteHistory(aUrl: String, timestamp: Long) = GlobalScope.future {
4964
storage.deleteVisit(aUrl, timestamp)
5065
notifyListeners()
5166
}
5267

68+
fun deleteVisitsFor(aUrl: String) = GlobalScope.future {
69+
storage.deleteVisitsFor(aUrl)
70+
notifyListeners()
71+
}
72+
73+
fun deleteEverything() = GlobalScope.future {
74+
storage.deleteEverything()
75+
notifyListeners()
76+
}
77+
78+
fun deleteVisitsSince(since: Long) = GlobalScope.future {
79+
storage.deleteVisitsSince(since)
80+
notifyListeners()
81+
}
82+
83+
fun deleteVisitsBetween(startTime: Long, endTime: Long) = GlobalScope.future {
84+
storage.deleteVisitsBetween(startTime, endTime)
85+
notifyListeners()
86+
}
87+
88+
fun getVisited(uris: List<String>) = GlobalScope.future {
89+
storage.getVisited(uris)
90+
}
91+
5392
fun isInHistory(aURL: String): CompletableFuture<Boolean> = GlobalScope.future {
5493
storage.getVisited(listOf(aURL)).isNotEmpty()
5594
}

app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStack.java

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import androidx.annotation.NonNull;
1818
import androidx.annotation.Nullable;
19+
import androidx.annotation.UiThread;
1920

2021
import org.mozilla.geckoview.AllowOrDeny;
2122
import org.mozilla.geckoview.ContentBlocking;
@@ -51,7 +52,8 @@
5152

5253
public class SessionStack implements ContentBlocking.Delegate, GeckoSession.NavigationDelegate,
5354
GeckoSession.ProgressDelegate, GeckoSession.ContentDelegate, GeckoSession.TextInputDelegate,
54-
GeckoSession.PromptDelegate, GeckoSession.MediaDelegate, SharedPreferences.OnSharedPreferenceChangeListener {
55+
GeckoSession.PromptDelegate, GeckoSession.MediaDelegate, GeckoSession.HistoryDelegate,
56+
SharedPreferences.OnSharedPreferenceChangeListener {
5557

5658
private static final String LOGTAG = "VRB";
5759

@@ -70,6 +72,7 @@ public class SessionStack implements ContentBlocking.Delegate, GeckoSession.Navi
7072
private LinkedHashMap<Integer, SessionState> mSessions;
7173
private transient GeckoSession.PermissionDelegate mPermissionDelegate;
7274
private transient GeckoSession.PromptDelegate mPromptDelegate;
75+
private transient GeckoSession.HistoryDelegate mHistoryDelegate;
7376
private int mPreviousGeckoSessionId = NO_SESSION;
7477
private String mRegion;
7578
private transient Context mContext;
@@ -206,6 +209,13 @@ public void setPromptDelegate(GeckoSession.PromptDelegate aDelegate) {
206209
}
207210
}
208211

212+
public void setHistoryDelegate(GeckoSession.HistoryDelegate aDelegate) {
213+
mHistoryDelegate = aDelegate;
214+
for (HashMap.Entry<Integer, SessionState> entry : mSessions.entrySet()) {
215+
entry.getValue().mSession.setHistoryDelegate(aDelegate);
216+
}
217+
}
218+
209219
public void addNavigationListener(GeckoSession.NavigationDelegate aListener) {
210220
mNavigationListeners.add(aListener);
211221
dumpState(mCurrentSession, aListener);
@@ -299,6 +309,7 @@ public void restore(SessionStack store, int currentSessionId) {
299309
state.mSession.setPromptDelegate(mPromptDelegate);
300310
state.mSession.setContentBlockingDelegate(this);
301311
state.mSession.setMediaDelegate(this);
312+
state.mSession.setHistoryDelegate(this);
302313
for (SessionChangeListener listener: mSessionChangeListeners) {
303314
listener.onNewSession(state.mSession, newSessionId);
304315
}
@@ -363,6 +374,7 @@ private int createSession(@NonNull SessionSettings aSettings) {
363374
state.mSession.setPromptDelegate(mPromptDelegate);
364375
state.mSession.setContentBlockingDelegate(this);
365376
state.mSession.setMediaDelegate(this);
377+
state.mSession.setHistoryDelegate(this);
366378
for (SessionChangeListener listener: mSessionChangeListeners) {
367379
listener.onNewSession(state.mSession, result);
368380
}
@@ -402,6 +414,7 @@ private void removeSession(int aSessionId) {
402414
session.setPermissionDelegate(null);
403415
session.setContentBlockingDelegate(null);
404416
session.setMediaDelegate(null);
417+
session.setHistoryDelegate(null);
405418
mSessions.remove(aSessionId);
406419
for (SessionChangeListener listener: mSessionChangeListeners) {
407420
listener.onRemoveSession(session, aSessionId);
@@ -952,16 +965,24 @@ public void onCanGoForward(@NonNull GeckoSession aSession, boolean aCanGoForward
952965
AtomicBoolean allowed = new AtomicBoolean(false);
953966
for (GeckoSession.NavigationDelegate listener: mNavigationListeners) {
954967
GeckoResult<AllowOrDeny> listenerResult = listener.onLoadRequest(aSession, aRequest);
955-
listenerResult.then(value -> {
956-
if (AllowOrDeny.ALLOW.equals(value)) {
957-
allowed.set(true);
958-
}
968+
if (listenerResult != null) {
969+
listenerResult.then(value -> {
970+
if (AllowOrDeny.ALLOW.equals(value)) {
971+
allowed.set(true);
972+
}
973+
if (count.getAndIncrement() == mNavigationListeners.size() - 1) {
974+
result.complete(allowed.get() ? AllowOrDeny.ALLOW : AllowOrDeny.DENY);
975+
}
976+
977+
return null;
978+
});
979+
980+
} else {
981+
allowed.set(true);
959982
if (count.getAndIncrement() == mNavigationListeners.size() - 1) {
960983
result.complete(allowed.get() ? AllowOrDeny.ALLOW : AllowOrDeny.DENY);
961984
}
962-
963-
return null;
964-
});
985+
}
965986
}
966987

967988
return result;
@@ -1334,6 +1355,35 @@ public void onMediaRemove(@NonNull GeckoSession session, @NonNull MediaElement e
13341355
}
13351356
}
13361357

1358+
// HistoryDelegate
1359+
1360+
@Override
1361+
public void onHistoryStateChange(@NonNull GeckoSession geckoSession, @NonNull GeckoSession.HistoryDelegate.HistoryList historyList) {
1362+
if (mHistoryDelegate != null) {
1363+
mHistoryDelegate.onHistoryStateChange(geckoSession, historyList);
1364+
}
1365+
}
1366+
1367+
@Nullable
1368+
@Override
1369+
public GeckoResult<Boolean> onVisited(@NonNull GeckoSession geckoSession, @NonNull String url, @Nullable String lastVisitedURL, int flags) {
1370+
if (mHistoryDelegate != null) {
1371+
return mHistoryDelegate.onVisited(geckoSession, url, lastVisitedURL, flags);
1372+
}
1373+
1374+
return GeckoResult.fromValue(false);
1375+
}
1376+
1377+
@UiThread
1378+
@Nullable
1379+
public GeckoResult<boolean[]> getVisited(@NonNull GeckoSession geckoSession, @NonNull String[] urls) {
1380+
if (mHistoryDelegate != null) {
1381+
return mHistoryDelegate.getVisited(geckoSession, urls);
1382+
}
1383+
1384+
return GeckoResult.fromValue(new boolean[]{});
1385+
}
1386+
13371387
// SharedPreferences.OnSharedPreferenceChangeListener
13381388

13391389
@Override

app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
import androidx.annotation.NonNull;
88
import androidx.databinding.BindingAdapter;
99

10+
import java.text.DateFormat;
11+
import java.text.SimpleDateFormat;
12+
import java.util.Calendar;
13+
import java.util.Date;
14+
import java.util.TimeZone;
15+
1016

1117
public class BindingAdapters {
1218

@@ -16,12 +22,12 @@ public static void showHide(@NonNull View view, boolean show) {
1622
}
1723

1824
@BindingAdapter("visibleInvisible")
19-
public static void showInvisible(View view, boolean show) {
25+
public static void showInvisible(@NonNull View view, boolean show) {
2026
view.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
2127
}
2228

2329
@BindingAdapter("typeface")
24-
public static void setTypeface(TextView v, String style) {
30+
public static void setTypeface(@NonNull TextView v, String style) {
2531
switch (style) {
2632
case "bold":
2733
v.setTypeface(null, Typeface.BOLD);
@@ -31,4 +37,20 @@ public static void setTypeface(TextView v, String style) {
3137
break;
3238
}
3339
}
40+
41+
@BindingAdapter("bindDate")
42+
public static void bindDate(@NonNull TextView textView, long timestamp) {
43+
String androidDateTime = android.text.format.DateFormat.getDateFormat(textView.getContext()).format(new Date(timestamp)) + " " +
44+
android.text.format.DateFormat.getTimeFormat(textView.getContext()).format(new Date(timestamp));
45+
String AmPm = "";
46+
if(!Character.isDigit(androidDateTime.charAt(androidDateTime.length()-1))) {
47+
if(androidDateTime.contains(new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM])){
48+
AmPm = " " + new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM];
49+
}else{
50+
AmPm = " " + new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.PM];
51+
}
52+
androidDateTime=androidDateTime.replace(AmPm, "");
53+
}
54+
textView.setText(androidDateTime.concat(AmPm));
55+
}
3456
}

app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
package org.mozilla.vrbrowser.ui.adapters;
22

3-
import android.animation.Animator;
4-
import android.animation.ValueAnimator;
3+
import android.annotation.SuppressLint;
54
import android.content.Context;
6-
import android.util.Log;
75
import android.view.LayoutInflater;
86
import android.view.MotionEvent;
97
import android.view.View;
108
import android.view.ViewGroup;
11-
import android.view.animation.AccelerateDecelerateInterpolator;
129
import android.widget.ImageView;
1310

11+
import androidx.annotation.NonNull;
12+
import androidx.annotation.Nullable;
13+
import androidx.databinding.DataBindingUtil;
14+
import androidx.recyclerview.widget.DiffUtil;
15+
import androidx.recyclerview.widget.RecyclerView;
16+
1417
import org.mozilla.vrbrowser.R;
1518
import org.mozilla.vrbrowser.databinding.BookmarkItemBinding;
1619
import org.mozilla.vrbrowser.ui.callbacks.BookmarkClickCallback;
1720
import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement;
21+
import org.mozilla.vrbrowser.utils.AnimationHelper;
1822

1923
import java.util.List;
2024
import java.util.Objects;
2125

22-
import androidx.annotation.NonNull;
23-
import androidx.annotation.Nullable;
24-
import androidx.databinding.DataBindingUtil;
25-
import androidx.recyclerview.widget.DiffUtil;
26-
import androidx.recyclerview.widget.RecyclerView;
27-
2826
import mozilla.components.concept.storage.BookmarkNode;
2927

3028
public class BookmarkAdapter extends RecyclerView.Adapter<BookmarkAdapter.BookmarkViewHolder> {
31-
static final String LOGTAG = "VRB";
29+
30+
static final String LOGTAG = BookmarkAdapter.class.getSimpleName();
31+
3232
private static final int ICON_ANIMATION_DURATION = 200;
3333

3434
private List<? extends BookmarkNode> mBookmarkList;
@@ -102,13 +102,14 @@ public int itemCount() {
102102
return mBookmarkList != null ? mBookmarkList.size() : 0;
103103
}
104104

105+
@SuppressLint("ClickableViewAccessibility")
105106
@Override
106107
public BookmarkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
107108
BookmarkItemBinding binding = DataBindingUtil
108109
.inflate(LayoutInflater.from(parent.getContext()), R.layout.bookmark_item,
109110
parent, false);
110111
binding.setCallback(mBookmarkClickCallback);
111-
binding.trash.setOnHoverListener(mTrashHoverListener);
112+
binding.trash.setOnHoverListener(mIconHoverListener);
112113
binding.trash.setOnTouchListener((view, motionEvent) -> {
113114
int ev = motionEvent.getActionMasked();
114115
switch (ev) {
@@ -151,20 +152,20 @@ static class BookmarkViewHolder extends RecyclerView.ViewHolder {
151152
}
152153
}
153154

154-
private View.OnHoverListener mTrashHoverListener = (view, motionEvent) -> {
155+
private View.OnHoverListener mIconHoverListener = (view, motionEvent) -> {
155156
ImageView icon = (ImageView)view;
156157
int ev = motionEvent.getActionMasked();
157158
switch (ev) {
158159
case MotionEvent.ACTION_HOVER_ENTER:
159160
icon.setColorFilter(mIconColorHover);
160-
animateViewPadding(view,
161+
AnimationHelper.animateViewPadding(view,
161162
mMaxPadding,
162163
mMinPadding,
163164
ICON_ANIMATION_DURATION);
164165
return false;
165166

166167
case MotionEvent.ACTION_HOVER_EXIT:
167-
animateViewPadding(view,
168+
AnimationHelper.animateViewPadding(view,
168169
mMinPadding,
169170
mMaxPadding,
170171
ICON_ANIMATION_DURATION,
@@ -175,46 +176,4 @@ static class BookmarkViewHolder extends RecyclerView.ViewHolder {
175176
return false;
176177
};
177178

178-
private void animateViewPadding(View view, int paddingStart, int paddingEnd, int duration) {
179-
animateViewPadding(view, paddingStart, paddingEnd, duration, null);
180-
}
181-
182-
private void animateViewPadding(View view, int paddingStart, int paddingEnd, int duration, Runnable onAnimationEnd) {
183-
ValueAnimator animation = ValueAnimator.ofInt(paddingStart, paddingEnd);
184-
animation.setDuration(duration);
185-
animation.setInterpolator(new AccelerateDecelerateInterpolator());
186-
animation.addUpdateListener(valueAnimator -> {
187-
try {
188-
int newPadding = Integer.parseInt(valueAnimator.getAnimatedValue().toString());
189-
view.setPadding(newPadding, newPadding, newPadding, newPadding);
190-
} catch (NumberFormatException ex) {
191-
Log.e(LOGTAG, "Error parsing BookmarkAdapter animation value: " + valueAnimator.getAnimatedValue().toString());
192-
}
193-
});
194-
animation.addListener(new Animator.AnimatorListener() {
195-
@Override
196-
public void onAnimationStart(Animator animator) {
197-
198-
}
199-
200-
@Override
201-
public void onAnimationEnd(Animator animator) {
202-
if (onAnimationEnd != null) {
203-
onAnimationEnd.run();
204-
}
205-
}
206-
207-
@Override
208-
public void onAnimationCancel(Animator animator) {
209-
210-
}
211-
212-
@Override
213-
public void onAnimationRepeat(Animator animator) {
214-
215-
}
216-
});
217-
animation.start();
218-
}
219-
220179
}

0 commit comments

Comments
 (0)