Skip to content

Commit

Permalink
Added support for bookmarks and history in the awesome bar (#1425)
Browse files Browse the repository at this point in the history
* Added support for bookmarks and history in the awesome bar
* Awesome bar icon style updates

As requested per design here: #962 (comment)
  • Loading branch information
keianhzo authored and MortimerGoro committed Jul 22, 2019
1 parent 0bb0c9e commit 6ccab7a
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 168 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.vrbrowser.browser

import android.content.Context
import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.future.future
import mozilla.components.concept.storage.VisitType
import org.mozilla.vrbrowser.VRBrowserApplication
import java.util.concurrent.CompletableFuture

class HistoryStore constructor(val context: Context) {
private var listeners = ArrayList<HistoryListener>()
private val storage = (context.applicationContext as VRBrowserApplication).places.history

interface HistoryListener {
fun onHistoryUpdated()
}

fun addListener(aListener: HistoryListener) {
if (!listeners.contains(aListener)) {
listeners.add(aListener)
}
}

fun removeListener(aListener: HistoryListener) {
listeners.remove(aListener)
}

fun removeAllListeners() {
listeners.clear()
}

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

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

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

fun isInHistory(aURL: String): CompletableFuture<Boolean> = GlobalScope.future {
storage.getVisited(listOf(aURL)) != null
}

private fun notifyListeners() {
if (listeners.size > 0) {
val listenersCopy = ArrayList(listeners)
Handler(Looper.getMainLooper()).post {
for (listener in listenersCopy) {
listener.onHistoryUpdated()
}
}
}
}
}

2 changes: 2 additions & 0 deletions app/src/common/shared/org/mozilla/vrbrowser/browser/Places.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ package org.mozilla.vrbrowser.browser

import android.content.Context
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
import mozilla.components.browser.storage.sync.PlacesHistoryStorage

/**
* Entry point for interacting with places-backed storage layers.
*/
class Places(context: Context) {
val bookmarks by lazy { PlacesBookmarksStorage(context) }
val history by lazy { PlacesHistoryStorage(context) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import mozilla.components.concept.storage.VisitType;

import static org.mozilla.vrbrowser.utils.ServoUtils.createServoSession;
import static org.mozilla.vrbrowser.utils.ServoUtils.isInstanceOfServoSession;
import static org.mozilla.vrbrowser.utils.ServoUtils.isServoAvailable;
Expand Down Expand Up @@ -134,6 +136,7 @@ class State {
private Context mContext;
private SharedPreferences mPrefs;
private BookmarksStore mBookmarksStore;
private HistoryStore mHistoryStore;

private SessionStore() {
mSessions = new LinkedHashMap<>();
Expand Down Expand Up @@ -170,6 +173,9 @@ public void unregisterListeners() {
if (mBookmarksStore != null) {
mBookmarksStore.removeAllListeners();
}
if (mHistoryStore!= null) {
mHistoryStore.removeAllListeners();
}
}

public void setContext(Context aContext, Bundle aExtras) {
Expand Down Expand Up @@ -208,6 +214,7 @@ public void setContext(Context aContext, Bundle aExtras) {
mContext = aContext;
mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mBookmarksStore = new BookmarksStore(mContext);
mHistoryStore = new HistoryStore(mContext);
if (mUserAgentOverride == null) {
mUserAgentOverride = new UserAgentOverride();
mUserAgentOverride.loadOverridesFromAssets((Activity)aContext, aContext.getString(R.string.user_agent_override_file));
Expand All @@ -218,6 +225,10 @@ public BookmarksStore getBookmarkStore() {
return mBookmarksStore;
}

public HistoryStore getHistoryStore() {
return mHistoryStore;
}

public void dumpAllState(Integer sessionId) {
dumpAllState(getSession(sessionId));
}
Expand Down Expand Up @@ -1045,6 +1056,11 @@ public void onCanGoForward(@NonNull GeckoSession aSession, boolean aCanGoForward

@Override
public @Nullable GeckoResult<AllowOrDeny> onLoadRequest(@NonNull GeckoSession aSession, @NonNull LoadRequest aRequest) {
if (aRequest.isRedirect)
mHistoryStore.addHistory(aRequest.uri, VisitType.EMBED);
else if (aRequest.triggerUri != null)
mHistoryStore.addHistory(aRequest.uri, VisitType.LINK);

final GeckoResult<AllowOrDeny> result = new GeckoResult<>();

String uri = aRequest.uri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager;

import androidx.annotation.NonNull;

import org.mozilla.vrbrowser.R;
import org.mozilla.vrbrowser.browser.SettingsStore;
import org.mozilla.vrbrowser.geolocation.GeolocationData;
Expand All @@ -19,8 +22,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import androidx.annotation.NonNull;
import java.util.concurrent.CompletableFuture;

import kotlinx.coroutines.Dispatchers;
import mozilla.components.browser.search.SearchEngine;
Expand Down Expand Up @@ -102,13 +104,14 @@ private String getSuggestionURL(String aQuery) {
return mSearchEngine.buildSuggestionsURL(aQuery);
}

public void getSuggestions(String aQuery, SuggestionsDelegate delegate) {
public CompletableFuture<List<String>> getSuggestions(String aQuery) {
CompletableFuture<List<String>> future = new CompletableFuture<>();
// TODO: Use mSuggestionsClient.getSuggestions when fixed in browser-search.
String query = getSuggestionURL(aQuery);
SuggestionsClient.getSuggestions(mSearchEngine, query, result -> {
delegate.OnSuggestions(result);
return null;
});
new Handler(mContext.getMainLooper()).post(() ->
SuggestionsClient.getSuggestions(mSearchEngine, query).thenAcceptAsync((result) -> future.complete(result)));

return future;
}

public String getResourceURL() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.loopj.android.http.TextHttpResponseHandler;

import java.util.List;
import java.util.function.Function;
import java.util.concurrent.CompletableFuture;

import cz.msebera.android.httpclient.Header;
import mozilla.components.browser.search.SearchEngine;
Expand All @@ -13,17 +13,21 @@ public class SuggestionsClient {

private static AsyncHttpClient client = new AsyncHttpClient();

public static void getSuggestions(SearchEngine mEngine, String aQuery, Function<List<String>, Void> callback) {
public static CompletableFuture<List<String>> getSuggestions(SearchEngine mEngine, String aQuery) {
final CompletableFuture future = new CompletableFuture();
client.cancelAllRequests(true);
client.get(aQuery, null, new TextHttpResponseHandler("ISO-8859-1") {
@Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
future.completeExceptionally(throwable);
}

@Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
callback.apply(SuggestionParser.selectResponseParser(mEngine).apply(responseString));
future.complete(SuggestionParser.selectResponseParser(mEngine).apply(responseString));
}
});

return future;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package org.mozilla.vrbrowser.search.suggestions;

import android.content.Context;

import androidx.annotation.NonNull;

import org.mozilla.vrbrowser.browser.SessionStore;
import org.mozilla.vrbrowser.search.SearchEngineWrapper;
import org.mozilla.vrbrowser.ui.widgets.SuggestionsWidget.SuggestionItem;
import org.mozilla.vrbrowser.ui.widgets.SuggestionsWidget.SuggestionItem.Type;
import org.mozilla.vrbrowser.utils.UrlUtils;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class SuggestionsProvider {

public class DefaultSuggestionsComparator implements Comparator {

public int compare(Object obj1, Object obj2) {
SuggestionItem suggestion1 = (SuggestionItem)obj1;
SuggestionItem suggestion2 = (SuggestionItem)obj2;
if (suggestion1.type == Type.SUGGESTION && suggestion2.type == Type.SUGGESTION) {
return 0;

} else if (suggestion1.type == suggestion2.type) {
if (mFilterText != null) {
if (suggestion1.title != null && suggestion2.title != null)
return suggestion1.title.toLowerCase().indexOf(mFilterText) - suggestion2.title.toLowerCase().indexOf(mFilterText);
return suggestion1.url.toLowerCase().indexOf(mFilterText) - suggestion2.url.indexOf(mFilterText);

} else {
return suggestion1.url.compareTo(suggestion2.url);
}

} else {
return suggestion1.type.ordinal() - suggestion2.type.ordinal();
}
}
}

private Context mContext;
private SearchEngineWrapper mSearchEngineWrapper;
private String mText;
private String mFilterText;
private Comparator mComparator;

public SuggestionsProvider(Context context) {
mContext = context;
mSearchEngineWrapper = SearchEngineWrapper.get(mContext);
mFilterText = "";
mComparator = new DefaultSuggestionsComparator();
}

private String getSearchURLOrDomain(String text) {
if (UrlUtils.isDomain(text)) {
return text;

} else {
return mSearchEngineWrapper.getSearchURL(text);
}
}

public void setFilterText(String text) {
mFilterText = text.toLowerCase();
}

public void setText(String text) { mText = text; }

public void setComparator(Comparator comparator) {
mComparator = comparator;
}

public CompletableFuture<List<SuggestionItem>> getBookmarkSuggestions(@NonNull List<SuggestionItem> items) {
CompletableFuture future = new CompletableFuture();
SessionStore.get().getBookmarkStore().getBookmarks().thenAcceptAsync((bookmarks) -> {
bookmarks.stream().
filter(b -> b.getUrl().toLowerCase().contains(mFilterText) ||
b.getTitle().toLowerCase().contains(mFilterText))
.forEach(b -> items.add(SuggestionItem.create(
b.getTitle(),
b.getUrl(),
null,
Type.BOOKMARK
)));
if (mComparator != null)
items.sort(mComparator);
future.complete(items);
});

return future;
}

public CompletableFuture<List<SuggestionItem>> getHistorySuggestions(@NonNull final List<SuggestionItem> items) {
CompletableFuture future = new CompletableFuture();
SessionStore.get().getHistoryStore().getHistory().thenAcceptAsync((history) -> {
history.stream()
.filter(h ->
h.toLowerCase().contains(mFilterText))
.forEach(h -> items.add(SuggestionItem.create(
h,
h,
null,
Type.HISTORY
)));
if (mComparator != null)
items.sort(mComparator);
future.complete(items);
});

return future;
}

public CompletableFuture<List<SuggestionItem>> getSearchEngineSuggestions(@NonNull final List<SuggestionItem> items) {
CompletableFuture future = new CompletableFuture();

// Completion from browser-domains
if (!mText.equals(mFilterText)) {
items.add(SuggestionItem.create(
mText,
getSearchURLOrDomain(mText),
null,
Type.COMPLETION
));
}

// Original text
items.add(SuggestionItem.create(
mFilterText,
getSearchURLOrDomain(mFilterText),
null,
Type.SUGGESTION
));

// Suggestions
mSearchEngineWrapper.getSuggestions(mFilterText).thenAcceptAsync((suggestions) -> {
suggestions.forEach(s -> {
String url = mSearchEngineWrapper.getSearchURL(s);
items.add(SuggestionItem.create(
s,
url,
null,
Type.SUGGESTION
));
});
if (mComparator != null)
items.sort(mComparator);
future.complete(items);
});

return future;
}

public CompletableFuture<List<SuggestionItem>> getSuggestions() {
return CompletableFuture.supplyAsync(() -> new ArrayList<SuggestionItem>())
.thenComposeAsync(this::getSearchEngineSuggestions)
.thenComposeAsync(this::getBookmarkSuggestions)
.thenComposeAsync(this::getHistorySuggestions);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import androidx.databinding.DataBindingUtil;

import mozilla.components.concept.storage.BookmarkNode;
import mozilla.components.concept.storage.VisitType;

public class BookmarksView extends FrameLayout implements GeckoSession.NavigationDelegate, BookmarksStore.BookmarkListener {

Expand Down Expand Up @@ -103,6 +104,7 @@ public void onClick(BookmarkNode bookmark) {
mAudio.playSound(AudioEngine.Sound.CLICK);
}

SessionStore.get().getHistoryStore().addHistory(bookmark.getUrl(), VisitType.BOOKMARK);
SessionStore.get().loadUri(bookmark.getUrl());
}

Expand Down

0 comments on commit 6ccab7a

Please sign in to comment.