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.
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
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
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));
}
}
@@ -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.