Skip to content

Commit

Permalink
History feed (#1656)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
keianhzo authored and bluemarvin committed Aug 27, 2019
1 parent f3ada54 commit d2f07b0
Show file tree
Hide file tree
Showing 54 changed files with 2,173 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.future
import mozilla.components.concept.storage.PageObservation
import mozilla.components.concept.storage.VisitInfo
import mozilla.components.concept.storage.VisitType
import org.mozilla.vrbrowser.VRBrowserApplication
import java.util.concurrent.CompletableFuture
Expand Down Expand Up @@ -40,16 +42,53 @@ class HistoryStore constructor(val context: Context) {
storage.getVisited()
}

fun addHistory(aURL: String, visitType: VisitType) = GlobalScope.future {
fun getDetailedHistory(): CompletableFuture<List<VisitInfo>?> = GlobalScope.future {
storage.getDetailedVisits(0, excludeTypes = listOf(
VisitType.NOT_A_VISIT,
VisitType.REDIRECT_TEMPORARY,
VisitType.REDIRECT_PERMANENT,
VisitType.RELOAD))
}

fun recordVisit(aURL: String, visitType: VisitType) = GlobalScope.future {
storage.recordVisit(aURL, visitType)
notifyListeners()
}

fun recordObservation(aURL: String, observation: PageObservation) = GlobalScope.future {
storage.recordObservation(aURL, observation)
notifyListeners()
}

fun deleteHistory(aUrl: String, timestamp: Long) = GlobalScope.future {
storage.deleteVisit(aUrl, timestamp)
notifyListeners()
}

fun deleteVisitsFor(aUrl: String) = GlobalScope.future {
storage.deleteVisitsFor(aUrl)
notifyListeners()
}

fun deleteEverything() = GlobalScope.future {
storage.deleteEverything()
notifyListeners()
}

fun deleteVisitsSince(since: Long) = GlobalScope.future {
storage.deleteVisitsSince(since)
notifyListeners()
}

fun deleteVisitsBetween(startTime: Long, endTime: Long) = GlobalScope.future {
storage.deleteVisitsBetween(startTime, endTime)
notifyListeners()
}

fun getVisited(uris: List<String>) = GlobalScope.future {
storage.getVisited(uris)
}

fun isInHistory(aURL: String): CompletableFuture<Boolean> = GlobalScope.future {
storage.getVisited(listOf(aURL)).isNotEmpty()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import org.mozilla.geckoview.AllowOrDeny;
import org.mozilla.geckoview.ContentBlocking;
Expand Down Expand Up @@ -51,7 +52,8 @@

public class SessionStack implements ContentBlocking.Delegate, GeckoSession.NavigationDelegate,
GeckoSession.ProgressDelegate, GeckoSession.ContentDelegate, GeckoSession.TextInputDelegate,
GeckoSession.PromptDelegate, GeckoSession.MediaDelegate, SharedPreferences.OnSharedPreferenceChangeListener {
GeckoSession.PromptDelegate, GeckoSession.MediaDelegate, GeckoSession.HistoryDelegate,
SharedPreferences.OnSharedPreferenceChangeListener {

private static final String LOGTAG = "VRB";

Expand All @@ -70,6 +72,7 @@ public class SessionStack implements ContentBlocking.Delegate, GeckoSession.Navi
private LinkedHashMap<Integer, SessionState> mSessions;
private transient GeckoSession.PermissionDelegate mPermissionDelegate;
private transient GeckoSession.PromptDelegate mPromptDelegate;
private transient GeckoSession.HistoryDelegate mHistoryDelegate;
private int mPreviousGeckoSessionId = NO_SESSION;
private String mRegion;
private transient Context mContext;
Expand Down Expand Up @@ -206,6 +209,13 @@ public void setPromptDelegate(GeckoSession.PromptDelegate aDelegate) {
}
}

public void setHistoryDelegate(GeckoSession.HistoryDelegate aDelegate) {
mHistoryDelegate = aDelegate;
for (HashMap.Entry<Integer, SessionState> entry : mSessions.entrySet()) {
entry.getValue().mSession.setHistoryDelegate(aDelegate);
}
}

public void addNavigationListener(GeckoSession.NavigationDelegate aListener) {
mNavigationListeners.add(aListener);
dumpState(mCurrentSession, aListener);
Expand Down Expand Up @@ -299,6 +309,7 @@ public void restore(SessionStack store, int currentSessionId) {
state.mSession.setPromptDelegate(mPromptDelegate);
state.mSession.setContentBlockingDelegate(this);
state.mSession.setMediaDelegate(this);
state.mSession.setHistoryDelegate(this);
for (SessionChangeListener listener: mSessionChangeListeners) {
listener.onNewSession(state.mSession, newSessionId);
}
Expand Down Expand Up @@ -363,6 +374,7 @@ private int createSession(@NonNull SessionSettings aSettings) {
state.mSession.setPromptDelegate(mPromptDelegate);
state.mSession.setContentBlockingDelegate(this);
state.mSession.setMediaDelegate(this);
state.mSession.setHistoryDelegate(this);
for (SessionChangeListener listener: mSessionChangeListeners) {
listener.onNewSession(state.mSession, result);
}
Expand Down Expand Up @@ -402,6 +414,7 @@ private void removeSession(int aSessionId) {
session.setPermissionDelegate(null);
session.setContentBlockingDelegate(null);
session.setMediaDelegate(null);
session.setHistoryDelegate(null);
mSessions.remove(aSessionId);
for (SessionChangeListener listener: mSessionChangeListeners) {
listener.onRemoveSession(session, aSessionId);
Expand Down Expand Up @@ -952,16 +965,24 @@ public void onCanGoForward(@NonNull GeckoSession aSession, boolean aCanGoForward
AtomicBoolean allowed = new AtomicBoolean(false);
for (GeckoSession.NavigationDelegate listener: mNavigationListeners) {
GeckoResult<AllowOrDeny> listenerResult = listener.onLoadRequest(aSession, aRequest);
listenerResult.then(value -> {
if (AllowOrDeny.ALLOW.equals(value)) {
allowed.set(true);
}
if (listenerResult != null) {
listenerResult.then(value -> {
if (AllowOrDeny.ALLOW.equals(value)) {
allowed.set(true);
}
if (count.getAndIncrement() == mNavigationListeners.size() - 1) {
result.complete(allowed.get() ? AllowOrDeny.ALLOW : AllowOrDeny.DENY);
}

return null;
});

} else {
allowed.set(true);
if (count.getAndIncrement() == mNavigationListeners.size() - 1) {
result.complete(allowed.get() ? AllowOrDeny.ALLOW : AllowOrDeny.DENY);
}

return null;
});
}
}

return result;
Expand Down Expand Up @@ -1334,6 +1355,35 @@ public void onMediaRemove(@NonNull GeckoSession session, @NonNull MediaElement e
}
}

// HistoryDelegate

@Override
public void onHistoryStateChange(@NonNull GeckoSession geckoSession, @NonNull GeckoSession.HistoryDelegate.HistoryList historyList) {
if (mHistoryDelegate != null) {
mHistoryDelegate.onHistoryStateChange(geckoSession, historyList);
}
}

@Nullable
@Override
public GeckoResult<Boolean> onVisited(@NonNull GeckoSession geckoSession, @NonNull String url, @Nullable String lastVisitedURL, int flags) {
if (mHistoryDelegate != null) {
return mHistoryDelegate.onVisited(geckoSession, url, lastVisitedURL, flags);
}

return GeckoResult.fromValue(false);
}

@UiThread
@Nullable
public GeckoResult<boolean[]> getVisited(@NonNull GeckoSession geckoSession, @NonNull String[] urls) {
if (mHistoryDelegate != null) {
return mHistoryDelegate.getVisited(geckoSession, urls);
}

return GeckoResult.fromValue(new boolean[]{});
}

// SharedPreferences.OnSharedPreferenceChangeListener

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
import androidx.annotation.NonNull;
import androidx.databinding.BindingAdapter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;


public class BindingAdapters {

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

@BindingAdapter("visibleInvisible")
public static void showInvisible(View view, boolean show) {
public static void showInvisible(@NonNull View view, boolean show) {
view.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}

@BindingAdapter("typeface")
public static void setTypeface(TextView v, String style) {
public static void setTypeface(@NonNull TextView v, String style) {
switch (style) {
case "bold":
v.setTypeface(null, Typeface.BOLD);
Expand All @@ -31,4 +37,20 @@ public static void setTypeface(TextView v, String style) {
break;
}
}

@BindingAdapter("bindDate")
public static void bindDate(@NonNull TextView textView, long timestamp) {
String androidDateTime = android.text.format.DateFormat.getDateFormat(textView.getContext()).format(new Date(timestamp)) + " " +
android.text.format.DateFormat.getTimeFormat(textView.getContext()).format(new Date(timestamp));
String AmPm = "";
if(!Character.isDigit(androidDateTime.charAt(androidDateTime.length()-1))) {
if(androidDateTime.contains(new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM])){
AmPm = " " + new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.AM];
}else{
AmPm = " " + new SimpleDateFormat().getDateFormatSymbols().getAmPmStrings()[Calendar.PM];
}
androidDateTime=androidDateTime.replace(AmPm, "");
}
textView.setText(androidDateTime.concat(AmPm));
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
package org.mozilla.vrbrowser.ui.adapters;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;

import org.mozilla.vrbrowser.R;
import org.mozilla.vrbrowser.databinding.BookmarkItemBinding;
import org.mozilla.vrbrowser.ui.callbacks.BookmarkClickCallback;
import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement;
import org.mozilla.vrbrowser.utils.AnimationHelper;

import java.util.List;
import java.util.Objects;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;

import mozilla.components.concept.storage.BookmarkNode;

public class BookmarkAdapter extends RecyclerView.Adapter<BookmarkAdapter.BookmarkViewHolder> {
static final String LOGTAG = "VRB";

static final String LOGTAG = BookmarkAdapter.class.getSimpleName();

private static final int ICON_ANIMATION_DURATION = 200;

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

@SuppressLint("ClickableViewAccessibility")
@Override
public BookmarkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
BookmarkItemBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.bookmark_item,
parent, false);
binding.setCallback(mBookmarkClickCallback);
binding.trash.setOnHoverListener(mTrashHoverListener);
binding.trash.setOnHoverListener(mIconHoverListener);
binding.trash.setOnTouchListener((view, motionEvent) -> {
int ev = motionEvent.getActionMasked();
switch (ev) {
Expand Down Expand Up @@ -151,20 +152,20 @@ static class BookmarkViewHolder extends RecyclerView.ViewHolder {
}
}

private View.OnHoverListener mTrashHoverListener = (view, motionEvent) -> {
private View.OnHoverListener mIconHoverListener = (view, motionEvent) -> {
ImageView icon = (ImageView)view;
int ev = motionEvent.getActionMasked();
switch (ev) {
case MotionEvent.ACTION_HOVER_ENTER:
icon.setColorFilter(mIconColorHover);
animateViewPadding(view,
AnimationHelper.animateViewPadding(view,
mMaxPadding,
mMinPadding,
ICON_ANIMATION_DURATION);
return false;

case MotionEvent.ACTION_HOVER_EXIT:
animateViewPadding(view,
AnimationHelper.animateViewPadding(view,
mMinPadding,
mMaxPadding,
ICON_ANIMATION_DURATION,
Expand All @@ -175,46 +176,4 @@ static class BookmarkViewHolder extends RecyclerView.ViewHolder {
return false;
};

private void animateViewPadding(View view, int paddingStart, int paddingEnd, int duration) {
animateViewPadding(view, paddingStart, paddingEnd, duration, null);
}

private void animateViewPadding(View view, int paddingStart, int paddingEnd, int duration, Runnable onAnimationEnd) {
ValueAnimator animation = ValueAnimator.ofInt(paddingStart, paddingEnd);
animation.setDuration(duration);
animation.setInterpolator(new AccelerateDecelerateInterpolator());
animation.addUpdateListener(valueAnimator -> {
try {
int newPadding = Integer.parseInt(valueAnimator.getAnimatedValue().toString());
view.setPadding(newPadding, newPadding, newPadding, newPadding);
} catch (NumberFormatException ex) {
Log.e(LOGTAG, "Error parsing BookmarkAdapter animation value: " + valueAnimator.getAnimatedValue().toString());
}
});
animation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {

}

@Override
public void onAnimationEnd(Animator animator) {
if (onAnimationEnd != null) {
onAnimationEnd.run();
}
}

@Override
public void onAnimationCancel(Animator animator) {

}

@Override
public void onAnimationRepeat(Animator animator) {

}
});
animation.start();
}

}

0 comments on commit d2f07b0

Please sign in to comment.