diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt b/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt index aaa7ecfbf..93c994df2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/BookmarksStore.kt @@ -21,6 +21,7 @@ class BookmarksStore constructor(val context: Context) { interface BookmarkListener { fun onBookmarksUpdated() + fun onBookmarkAdded() } fun addListener(aListener: BookmarkListener) { @@ -44,6 +45,7 @@ class BookmarksStore constructor(val context: Context) { fun addBookmark(aURL: String, aTitle: String) = GlobalScope.future { storage.addItem(BookmarkRoot.Mobile.id, aURL, aTitle, null) notifyListeners() + notifyAddedListeners() } fun deleteBookmarkByURL(aURL: String) = GlobalScope.future { @@ -89,4 +91,15 @@ class BookmarksStore constructor(val context: Context) { } } } + + private fun notifyAddedListeners() { + if (listeners.size > 0) { + val listenersCopy = ArrayList(listeners) + Handler(Looper.getMainLooper()).post { + for (listener in listenersCopy) { + listener.onBookmarkAdded() + } + } + } + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java index 17cabbf1c..208405cb0 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BindingAdapters.java @@ -1,17 +1,20 @@ package org.mozilla.vrbrowser.ui.adapters; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ImageSpan; import android.view.View; import android.widget.TextView; +import androidx.annotation.DrawableRes; 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 { @@ -53,4 +56,13 @@ public static void bindDate(@NonNull TextView textView, long timestamp) { } textView.setText(androidDateTime.concat(AmPm)); } + + @BindingAdapter(value={"textDrawable", "textString"}) + public static void setSpannableString(@NonNull TextView textView, Drawable drawable, String text) { + SpannableString spannableString = new SpannableString(text); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); + spannableString.setSpan(span, spannableString.toString().indexOf("@"), spannableString.toString().indexOf("@")+1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + textView.setText(spannableString); + } } \ No newline at end of file diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java index a96bada13..43b828851 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/BookmarkAdapter.java @@ -16,7 +16,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.databinding.BookmarkItemBinding; -import org.mozilla.vrbrowser.ui.callbacks.BookmarkClickCallback; +import org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.AnimationHelper; @@ -39,13 +39,13 @@ public class BookmarkAdapter extends RecyclerView.Adapter { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_HOVER_ENTER: + binding.setIsHovered(true); + return false; + + case MotionEvent.ACTION_HOVER_EXIT: + binding.setIsHovered(false); + return false; + } + + return false; + }); + binding.layout.setOnTouchListener((view, motionEvent) -> { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_UP: + return false; + + case MotionEvent.ACTION_DOWN: + binding.setIsHovered(true); + return false; + } + return false; + }); + binding.more.setOnHoverListener(mIconHoverListener); + binding.more.setOnTouchListener((view, motionEvent) -> { + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_UP: + mBookmarkItemCallback.onMore(view, binding.getItem()); + return true; + + case MotionEvent.ACTION_DOWN: + return true; + } + return false; + }); binding.trash.setOnHoverListener(mIconHoverListener); binding.trash.setOnTouchListener((view, motionEvent) -> { int ev = motionEvent.getActionMasked(); switch (ev) { case MotionEvent.ACTION_UP: - mBookmarkClickCallback.onDelete(binding.getBookmark()); + mBookmarkItemCallback.onDelete(view, binding.getItem()); return true; case MotionEvent.ACTION_DOWN: @@ -127,7 +174,7 @@ public BookmarkViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int view @Override public void onBindViewHolder(@NonNull BookmarkViewHolder holder, int position) { - holder.binding.setBookmark(mBookmarkList.get(position)); + holder.binding.setItem(mBookmarkList.get(position)); holder.binding.executePendingBindings(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java index abb282295..50c2a5d2e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/adapters/HistoryAdapter.java @@ -44,8 +44,8 @@ public class HistoryAdapter extends RecyclerView.Adapter { + v.requestFocusFromTouch(); + return false; + }); mBinding.setIsLoading(true); mBinding.executePendingBindings(); syncBookmarks(); SessionStore.get().getBookmarkStore().addListener(this); setVisibility(GONE); + + setOnTouchListener((v, event) -> { + v.requestFocusFromTouch(); + return false; + }); } public void onDestroy() { SessionStore.get().getBookmarkStore().removeListener(this); } - private final BookmarkClickCallback mBookmarkClickCallback = new BookmarkClickCallback() { + private final BookmarkItemCallback mBookmarkItemCallback = new BookmarkItemCallback() { @Override - public void onClick(BookmarkNode bookmark) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + public void onClick(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); SessionStack sessionStack = SessionStore.get().getActiveStore(); - sessionStack.loadUri(bookmark.getUrl()); + sessionStack.loadUri(item.getUrl()); } @Override - public void onDelete(BookmarkNode bookmark) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + public void onDelete(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); mIgnoreNextListener = true; - SessionStore.get().getBookmarkStore().deleteBookmarkById(bookmark.getGuid()); - mBookmarkAdapter.removeItem(bookmark); + SessionStore.get().getBookmarkStore().deleteBookmarkById(item.getGuid()); + mBookmarkAdapter.removeItem(item); if (mBookmarkAdapter.itemCount() == 0) { mBinding.setIsEmpty(true); mBinding.setIsLoading(false); mBinding.executePendingBindings(); } } + + @Override + public void onMore(View view, BookmarkNode item) { + mBinding.bookmarksList.requestFocusFromTouch(); + + int rowPosition = mBookmarkAdapter.getItemPosition(item.getGuid()); + RecyclerView.ViewHolder row = mBinding.bookmarksList.findViewHolderForLayoutPosition(rowPosition); + boolean isLastVisibleItem = false; + if (mBinding.bookmarksList.getLayoutManager() instanceof LinearLayoutManager) { + LinearLayoutManager layoutManager = (LinearLayoutManager) mBinding.bookmarksList.getLayoutManager(); + if (rowPosition == layoutManager.findLastVisibleItemPosition()) { + isLastVisibleItem = true; + } + } + + mBinding.getCallback().onShowContextMenu( + row.itemView, + item, + isLastVisibleItem); + } }; + public void setBookmarksCallback(@NonNull BookmarksCallback callback) { + mBinding.setCallback(callback); + } private void syncBookmarks() { SessionStore.get().getBookmarkStore().getBookmarks().thenAcceptAsync(this::showBookmarks, new UIThreadExecutor()); @@ -113,6 +146,8 @@ private void showBookmarks(List aBookmarks) { mBookmarkAdapter.setBookmarkList(aBookmarks); } mBinding.executePendingBindings(); + mBinding.bookmarksList.post(() -> mBinding.bookmarksList.smoothScrollToPosition( + mBookmarkAdapter.getItemCount() > 0 ? mBookmarkAdapter.getItemCount() - 1 : 0)); } // BookmarksStore.BookmarksViewListener @@ -124,4 +159,9 @@ public void onBookmarksUpdated() { } syncBookmarks(); } + + @Override + public void onBookmarkAdded() { + // Nothing to do + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java deleted file mode 100644 index 47bc9e585..000000000 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryItemContextMenu.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- 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.ui.views; - -import android.content.Context; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.widget.FrameLayout; - -import androidx.databinding.DataBindingUtil; - -import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.browser.engine.SessionStore; -import org.mozilla.vrbrowser.databinding.HistoryItemContextMenuBinding; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; - -import mozilla.components.concept.storage.VisitInfo; - -public class HistoryItemContextMenu extends FrameLayout { - - private static final String LOGTAG = HistoryItemContextMenu.class.getSimpleName(); - - private HistoryItemContextMenuBinding mBinding; - - public HistoryItemContextMenu(Context aContext) { - super(aContext); - initialize(aContext); - } - - public HistoryItemContextMenu(Context aContext, AttributeSet aAttrs) { - super(aContext, aAttrs); - initialize(aContext); - } - - public HistoryItemContextMenu(Context aContext, AttributeSet aAttrs, int aDefStyle) { - super(aContext, aAttrs, aDefStyle); - initialize(aContext); - } - - private void initialize(Context aContext) { - LayoutInflater inflater = LayoutInflater.from(aContext); - - mBinding = DataBindingUtil.inflate(inflater, R.layout.history_item_context_menu, this, true); - } - - public void setItem(VisitInfo item) { - SessionStore.get().getBookmarkStore().isBookmarked(item.getUrl()).thenAccept((isBookmarked -> { - mBinding.setItem(item); - mBinding.setIsBookmarked(isBookmarked); - mBinding.bookmark.setText(isBookmarked ? R.string.history_context_remove_bookmarks : R.string.history_context_add_bookmarks); - invalidate(); - - })).exceptionally(throwable -> { - Log.d(LOGTAG, "Couldn't get the bookmarked status of the history item"); - return null; - }); - } - - public void setContextMenuClickCallback(HistoryItemContextMenuClickCallback callback) { - mBinding.setCallback(callback); - } - -} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java index e9e333b21..2918ea2fb 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/HistoryView.java @@ -92,7 +92,8 @@ public void onDestroy() { private final HistoryItemCallback mHistoryItemCallback = new HistoryItemCallback() { @Override public void onClick(View view, VisitInfo item) { - view.requestFocusFromTouch(); + mBinding.historyList.requestFocusFromTouch(); + SessionStack sessionStack = SessionStore.get().getActiveStore(); sessionStack.loadUri(item.getUrl()); } @@ -100,6 +101,7 @@ public void onClick(View view, VisitInfo item) { @Override public void onDelete(View view, VisitInfo item) { mBinding.historyList.requestFocusFromTouch(); + mIgnoreNextListener = true; SessionStore.get().getHistoryStore().deleteHistory(item.getUrl(), item.getVisitTime()); mHistoryAdapter.removeItem(item); @@ -112,7 +114,7 @@ public void onDelete(View view, VisitInfo item) { @Override public void onMore(View view, VisitInfo item) { - view.requestFocusFromTouch(); + mBinding.historyList.requestFocusFromTouch(); int rowPosition = mHistoryAdapter.getItemPosition(item.getVisitTime()); RecyclerView.ViewHolder row = mBinding.historyList.findViewHolderForLayoutPosition(rowPosition); @@ -193,6 +195,7 @@ private void showHistory(List historyItems) { mHistoryAdapter.setHistoryList(historyItems); } mBinding.executePendingBindings(); + mBinding.historyList.post(() -> mBinding.historyList.smoothScrollToPosition(0)); } // HistoryStore.HistoryListener diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java new file mode 100644 index 000000000..c217f9a40 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/LibraryItemContextMenu.java @@ -0,0 +1,113 @@ +/* -*- 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.ui.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.databinding.DataBindingUtil; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.browser.engine.SessionStore; +import org.mozilla.vrbrowser.databinding.LibraryItemContextMenuBinding; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +public class LibraryItemContextMenu extends FrameLayout { + + private static final String LOGTAG = LibraryItemContextMenu.class.getSimpleName(); + + public enum LibraryItemType { + BOOKMARKS, + HISTORY + } + + public static class LibraryContextMenuItem { + + private String url; + private String title; + private LibraryItemType type; + + public LibraryContextMenuItem(@NonNull String url, String title, LibraryItemType type) { + this.url = url; + this.title = title; + this.type = type; + } + + public String getUrl() { + return url; + } + + public String getTitle() { + return title; + } + + public LibraryItemType getType() { + return type; + } + + } + + private LibraryItemContextMenuBinding mBinding; + + public LibraryItemContextMenu(Context aContext) { + super(aContext); + initialize(aContext); + } + + public LibraryItemContextMenu(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public LibraryItemContextMenu(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + private void initialize(Context aContext) { + LayoutInflater inflater = LayoutInflater.from(aContext); + + mBinding = DataBindingUtil.inflate(inflater, R.layout.library_item_context_menu, this, true); + } + + public void setItem(@NonNull LibraryContextMenuItem item) { + SessionStore.get().getBookmarkStore().isBookmarked(item.getUrl()).thenAccept((isBookmarked -> { + mBinding.setItem(item); + mBinding.setIsBookmarked(isBookmarked); + mBinding.bookmark.setText(isBookmarked ? R.string.history_context_remove_bookmarks : R.string.history_context_add_bookmarks); + invalidate(); + + })).exceptionally(throwable -> { + Log.d(LOGTAG, "Couldn't get the bookmarked status of the history item"); + return null; + }); + } + + public void setContextMenuClickCallback(LibraryItemContextMenuClickCallback callback) { + mBinding.setCallback(callback); + } + + public int getMenuHeight() { + switch (mBinding.getItem().getType()) { + case BOOKMARKS: + mBinding.bookmarkLayout.setVisibility(GONE); + mBinding.newWindowLayout.setBackgroundResource(R.drawable.library_context_menu_item_background_single); + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height); + case HISTORY: + mBinding.bookmarkLayout.setVisibility(VISIBLE); + mBinding.newWindowLayout.setBackgroundResource(R.drawable.library_context_menu_item_background_top); + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height) * 2; + } + + return WidgetPlacement.dpDimension(getContext(), R.dimen.library_item_row_height) * 2; + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java index d3dd988e8..bdf426e17 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/UIButton.java @@ -9,6 +9,8 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; +import android.graphics.PointF; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; @@ -22,6 +24,7 @@ import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.ui.widgets.TooltipWidget; import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.ViewUtils; public class UIButton extends AppCompatImageButton implements CustomUIButton { @@ -29,7 +32,8 @@ public class UIButton extends AppCompatImageButton implements CustomUIButton { private enum State { NORMAL, PRIVATE, - ACTIVE + ACTIVE, + NOTIFICATION } private ColorStateList mTintColorList; @@ -39,6 +43,7 @@ private enum State { private @IdRes int mTintColorListRes; private @IdRes int mPrivateModeTintColorListRes; private @IdRes int mActiveModeTintColorListRes; + private @IdRes int mNotificationModeTintColorListRes; private TooltipWidget mTooltipView; private String mTooltipText; private State mState; @@ -63,6 +68,7 @@ public UIButton(Context context, AttributeSet attrs, int defStyleAttr) { mActiveModeBackground = attributes.getDrawable(R.styleable.UIButton_activeModeBackground); mPrivateModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_privateModeTintColorList, 0); mActiveModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_activeModeTintColorList, 0); + mNotificationModeTintColorListRes = attributes.getResourceId(R.styleable.UIButton_notificationModeTintColorList, 0); mTooltipDelay = attributes.getInt(R.styleable.UIButton_tooltipDelay, getResources().getInteger(R.integer.tooltip_delay)); mTooltipPosition = ViewUtils.TooltipPosition.fromId(attributes.getInt(R.styleable.UIButton_tooltipPosition, ViewUtils.TooltipPosition.BOTTOM.ordinal())); mTooltipDensity = attributes.getFloat(R.styleable.UIButton_tooltipDensity, getContext().getResources().getDisplayMetrics().density); @@ -163,6 +169,15 @@ public void setActiveMode(boolean isActive) { } } + public void setNotificationMode(boolean isNotification) { + if (isNotification) { + setNotification(); + + } else { + setNormal(); + } + } + public boolean isActive() { return mState == State.ACTIVE; } @@ -204,6 +219,13 @@ private void setActive() { } } + private void setNotification() { + mState = State.NOTIFICATION; + if (mActiveModeTintColorListRes != 0) { + setTintColorList(mNotificationModeTintColorListRes); + } + } + private Runnable mShowTooltipRunnable = new Runnable() { @Override public void run() { @@ -214,7 +236,31 @@ public void run() { mTooltipView = new TooltipWidget(getContext()); mTooltipView.setCurvedMode(mCurvedTooltip); mTooltipView.setText(getTooltip()); - mTooltipView.setLayoutParams(UIButton.this, mTooltipPosition, mTooltipDensity); + + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + UIWidget parent = ViewUtils.getParentWidget(UIButton.this); + parent.offsetDescendantRectToMyCoords(UIButton.this, offsetViewBounds); + + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), parent); + + mTooltipView.getPlacement().parentHandle = parent.getHandle(); + mTooltipView.getPlacement().density = mTooltipDensity; + // At the moment we only support showing tooltips on top or bottom of the target view + if (mTooltipPosition == ViewUtils.TooltipPosition.BOTTOM) { + mTooltipView.getPlacement().anchorY = 1.0f; + mTooltipView.getPlacement().parentAnchorY = 0.0f; + mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getWidth() / 2.0f) * ratio; + mTooltipView.getPlacement().translationY = -offsetViewBounds.top * ratio; + + } else { + mTooltipView.getPlacement().anchorY = 0.0f; + mTooltipView.getPlacement().parentAnchorY = 1.0f; + mTooltipView.getPlacement().translationX = (offsetViewBounds.left + UIButton.this.getHeight() / 2.0f) * ratio; + mTooltipView.getPlacement().translationY = offsetViewBounds.top * ratio; + } + + mTooltipView.setCurvedMode(false); mTooltipView.show(UIWidget.CLEAR_FOCUS); } }; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java index 95fa284fa..bd0cb5656 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TooltipWidget.java @@ -1,32 +1,37 @@ package org.mozilla.vrbrowser.ui.widgets; import android.content.Context; -import android.graphics.PointF; -import android.graphics.Rect; -import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.utils.ViewUtils; +import org.mozilla.vrbrowser.utils.AnimationHelper; public class TooltipWidget extends UIWidget { - private View mTargetView; - private UIWidget mParentWidget; protected TextView mText; - private PointF mTranslation; - private float mRatio; - private float mDensityRatio; + protected ViewGroup mLayout; + + public TooltipWidget(@NonNull Context aContext, @NonNull @LayoutRes int layoutRes) { + super(aContext); + + initialize(layoutRes); + } public TooltipWidget(Context aContext) { super(aContext); - initialize(); + initialize(R.layout.tooltip); } - private void initialize() { - inflate(getContext(), R.layout.tooltip, this); + private void initialize(@NonNull @LayoutRes int layoutRes) { + inflate(getContext(), layoutRes, this); + mLayout = findViewById(R.id.layout); mText = findViewById(R.id.tooltipText); } @@ -46,58 +51,28 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { public void show(@ShowFlags int aShowFlags) { measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mWidgetPlacement.translationX = mTranslation.x * (mRatio / mWidgetPlacement.density); - mWidgetPlacement.translationY = mTranslation.y * (mRatio / mWidgetPlacement.density); int paddingH = getPaddingStart() + getPaddingEnd(); int paddingV = getPaddingTop() + getPaddingBottom(); - mWidgetPlacement.width = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredWidth() + paddingH)/mDensityRatio); - mWidgetPlacement.height = (int)(WidgetPlacement.convertPixelsToDp(getContext(), getMeasuredHeight() + paddingV)/mDensityRatio); + mWidgetPlacement.width = (int)((getMeasuredWidth() + paddingH)/mWidgetPlacement.density); + mWidgetPlacement.height = (int)((getMeasuredHeight() + paddingV)/mWidgetPlacement.density); super.show(aShowFlags); + AnimationHelper.scaleIn(mLayout, 100, 0, null); } - public void setLayoutParams(View targetView) { - this.setLayoutParams(targetView, ViewUtils.TooltipPosition.BOTTOM); - } - - public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position) { - this.setLayoutParams(targetView, position, mWidgetPlacement.density); - } - - public void setLayoutParams(View targetView, ViewUtils.TooltipPosition position, float density) { - mTargetView = targetView; - mParentWidget = ViewUtils.getParentWidget(mTargetView); - if (mParentWidget != null) { - mRatio = WidgetPlacement.worldToWidgetRatio(mParentWidget); - mWidgetPlacement.density = density; - mDensityRatio = mWidgetPlacement.density / getContext().getResources().getDisplayMetrics().density; - - Rect offsetViewBounds = new Rect(); - getDrawingRect(offsetViewBounds); - mParentWidget.offsetDescendantRectToMyCoords(mTargetView, offsetViewBounds); - - mWidgetPlacement.parentHandle = mParentWidget.getHandle(); - // At the moment we only support showing tooltips on top or bottom of the target view - if (position == ViewUtils.TooltipPosition.BOTTOM) { - mWidgetPlacement.anchorY = 1.0f; - mWidgetPlacement.parentAnchorY = 0.0f; - mTranslation = new PointF( - (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio, - -offsetViewBounds.top * mDensityRatio); - } else { - mWidgetPlacement.anchorY = 0.0f; - mWidgetPlacement.parentAnchorY = 1.0f; - mTranslation = new PointF( - (offsetViewBounds.left + mTargetView.getWidth() / 2) * mDensityRatio, - offsetViewBounds.top * mDensityRatio); - } - } + @Override + public void hide(int aHideFlags) { + AnimationHelper.scaleOut(mLayout, 100, 0, () -> TooltipWidget.super.hide(aHideFlags)); } public void setCurvedMode(boolean enabled) { mWidgetPlacement.cylinder = enabled; } + public void setText(@StringRes int stringRes) { + mText.setText(stringRes); + } + public void setText(String text) { mText.setText(text); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java index a70071633..0859fba81 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TrayWidget.java @@ -8,6 +8,8 @@ import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; +import android.graphics.PointF; +import android.graphics.Rect; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; @@ -16,11 +18,14 @@ import androidx.annotation.NonNull; +import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.audio.AudioEngine; +import org.mozilla.vrbrowser.browser.BookmarksStore; import org.mozilla.vrbrowser.browser.SessionChangeListener; import org.mozilla.vrbrowser.browser.engine.SessionStack; +import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; import org.mozilla.vrbrowser.ui.views.UIButton; import org.mozilla.vrbrowser.ui.widgets.settings.SettingsWidget; @@ -35,6 +40,7 @@ public class TrayWidget extends UIWidget implements SessionChangeListener, Windo static final String LOGTAG = TrayWidget.class.getSimpleName(); private static final int ICON_ANIMATION_DURATION = 200; + private static final int LIBRARY_NOTIFICATION_DURATION = 3000; private UIButton mSettingsButton; private UIButton mPrivateButton; @@ -50,6 +56,7 @@ public class TrayWidget extends UIWidget implements SessionChangeListener, Windo private boolean mTrayVisible = true; private SessionStack mSessionStack; private WindowWidget mAttachedWindow; + private TooltipWidget mLibraryNotification; public TrayWidget(Context aContext) { super(aContext); @@ -291,6 +298,7 @@ public void detachFromWindow() { mSessionStack = null; } if (mAttachedWindow != null) { + SessionStore.get().getBookmarkStore().addListener(mBookmarksListener); mAttachedWindow.removeBookmarksViewListener(this); mAttachedWindow.removeHistoryViewListener(this); } @@ -310,6 +318,8 @@ public void attachToWindow(@NonNull WindowWidget aWindow) { mAttachedWindow.addBookmarksViewListener(this); mAttachedWindow.addHistoryViewListener(this); + SessionStore.get().getBookmarkStore().addListener(mBookmarksListener); + mSessionStack = aWindow.getSessionStack(); if (mSessionStack != null) { mSessionStack.addSessionChangeListener(this); @@ -441,4 +451,46 @@ public void onWidgetUpdate(Widget aWidget) { } } + private BookmarksStore.BookmarkListener mBookmarksListener = new BookmarksStore.BookmarkListener() { + @Override + public void onBookmarksUpdated() { + // Nothing to do + } + + @Override + public void onBookmarkAdded() { + mBookmarksButton.setNotificationMode(true); + ThreadUtils.postToUiThread(() -> { + if (mLibraryNotification != null && mLibraryNotification.isVisible()) { + return; + } + + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + offsetDescendantRectToMyCoords(mBookmarksButton, offsetViewBounds); + + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), TrayWidget.this); + + mLibraryNotification = new TooltipWidget(getContext(), R.layout.library_notification); + mLibraryNotification.getPlacement().parentHandle = getHandle(); + mLibraryNotification.getPlacement().anchorY = 0.0f; + mLibraryNotification.getPlacement().translationX = (offsetViewBounds.left + mBookmarksButton.getWidth() / 2.0f) * ratio; + mLibraryNotification.getPlacement().translationY = ((offsetViewBounds.top - 60) * ratio); + mLibraryNotification.getPlacement().translationZ = 25.0f; + mLibraryNotification.getPlacement().density = 3.0f; + mLibraryNotification.setText(R.string.bookmarks_saved_notification); + mLibraryNotification.setCurvedMode(false); + mLibraryNotification.show(UIWidget.CLEAR_FOCUS); + + ThreadUtils.postDelayedToUiThread(() -> { + if (mLibraryNotification != null) { + mLibraryNotification.hide(UIWidget.REMOVE_WIDGET); + } + mBookmarksButton.setNotificationMode(false); + }, LIBRARY_NOTIFICATION_DURATION); + }); + } + }; + + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java index eb207addf..b58ec1a16 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java @@ -25,6 +25,7 @@ import androidx.annotation.StringRes; import androidx.annotation.UiThread; +import org.jetbrains.annotations.NotNull; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.GeckoDisplay; import org.mozilla.geckoview.GeckoResult; @@ -37,14 +38,16 @@ import org.mozilla.vrbrowser.browser.engine.SessionStack; import org.mozilla.vrbrowser.browser.engine.SessionStore; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.ui.callbacks.BookmarksCallback; import org.mozilla.vrbrowser.ui.callbacks.HistoryCallback; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; import org.mozilla.vrbrowser.ui.views.BookmarksView; import org.mozilla.vrbrowser.ui.views.HistoryView; +import org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu; import org.mozilla.vrbrowser.ui.widgets.dialogs.BaseAppDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.ClearCacheDialogWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.ContextMenuWidget; -import org.mozilla.vrbrowser.ui.widgets.dialogs.HistoryItemContextMenuWidget; +import org.mozilla.vrbrowser.ui.widgets.dialogs.LibraryItemContextMenuWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MaxWindowsWidget; import org.mozilla.vrbrowser.ui.widgets.dialogs.MessageDialogWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; @@ -61,6 +64,7 @@ import java.util.Calendar; import java.util.GregorianCalendar; +import mozilla.components.concept.storage.BookmarkNode; import mozilla.components.concept.storage.PageObservation; import mozilla.components.concept.storage.VisitInfo; import mozilla.components.concept.storage.VisitType; @@ -76,12 +80,12 @@ public class WindowWidget extends UIWidget implements SessionChangeListener, public interface HistoryViewDelegate { default void onHistoryViewShown(WindowWidget aWindow) {} - default void onHistoryViewHidden(WindowWidget aWindow) {}; + default void onHistoryViewHidden(WindowWidget aWindow) {} } public interface BookmarksViewDelegate { - default void onBookmarksShown(WindowWidget aWindow) {}; - default void onBookmarksHidden(WindowWidget aWindow) {}; + default void onBookmarksShown(WindowWidget aWindow) {} + default void onBookmarksHidden(WindowWidget aWindow) {} } private int mSessionId; @@ -104,7 +108,7 @@ public interface BookmarksViewDelegate { private MessageDialogWidget mAppDialog; private ClearCacheDialogWidget mClearCacheDialog; private ContextMenuWidget mContextMenu; - private HistoryItemContextMenuWidget mHistoryContextMenu; + private LibraryItemContextMenuWidget mLibraryItemContextMenu; private int mWidthBackup; private int mHeightBackup; private int mBorderWidth; @@ -150,6 +154,7 @@ public WindowWidget(Context aContext, int windowId, boolean privateMode) { mSessionStack.newSession(); mBookmarksView = new BookmarksView(aContext); + mBookmarksView.setBookmarksCallback(mBookmarksCallback); mBookmarksViewListeners = new ArrayList<>(); mHistoryView = new HistoryView(aContext); @@ -262,45 +267,54 @@ protected void setRestored(boolean restored) { mIsRestored = restored; } - private void setView(View view) { - pauseCompositor(); + private void setView(View view, boolean switchSurface) { + if (switchSurface) { + pauseCompositor(); + } + mView = view; removeView(view); mView.setVisibility(VISIBLE); addView(mView); - mWidgetPlacement.density = getContext().getResources().getDisplayMetrics().density; - if (mTexture != null && mSurface != null && mRenderer == null) { - // Create the UI Renderer for the current surface. - // Surface must be released when switching back to WebView surface or the browser - // will not render it correctly. See release code in unsetView(). - mRenderer = new UISurfaceTextureRenderer(mSurface, mWidgetPlacement.textureWidth(), mWidgetPlacement.textureHeight()); + + if (switchSurface) { + mWidgetPlacement.density = getContext().getResources().getDisplayMetrics().density; + if (mTexture != null && mSurface != null && mRenderer == null) { + // Create the UI Renderer for the current surface. + // Surface must be released when switching back to WebView surface or the browser + // will not render it correctly. See release code in unsetView(). + mRenderer = new UISurfaceTextureRenderer(mSurface, mWidgetPlacement.textureWidth(), mWidgetPlacement.textureHeight()); + } + mWidgetManager.updateWidget(this); + mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); + mWidgetManager.pushBackHandler(mBackHandler); + setWillNotDraw(false); + postInvalidate(); } - mWidgetManager.updateWidget(this); - mWidgetManager.pushWorldBrightness(this, WidgetManagerDelegate.DEFAULT_DIM_BRIGHTNESS); - mWidgetManager.pushBackHandler(mBackHandler); - setWillNotDraw(false); - postInvalidate(); } - private void unsetView(View view) { + private void unsetView(View view, boolean switchSurface) { if (mView != null && mView == view) { mView = null; removeView(view); view.setVisibility(GONE); - setWillNotDraw(true); - if (mTexture != null) { - // Surface must be recreated here when not using layers. - // When using layers the new Surface is received via the setSurface() method. - if (mRenderer != null) { - mRenderer.release(); - mRenderer = null; + + if (switchSurface) { + setWillNotDraw(true); + if (mTexture != null) { + // Surface must be recreated here when not using layers. + // When using layers the new Surface is received via the setSurface() method. + if (mRenderer != null) { + mRenderer.release(); + mRenderer = null; + } + mSurface = new Surface(mTexture); } - mSurface = new Surface(mTexture); + mWidgetPlacement.density = 1.0f; + mWidgetManager.updateWidget(this); + mWidgetManager.popWorldBrightness(this); + mWidgetManager.popBackHandler(mBackHandler); } - mWidgetPlacement.density = 1.0f; - mWidgetManager.updateWidget(this); - mWidgetManager.popWorldBrightness(this); - mWidgetManager.popBackHandler(mBackHandler); } } @@ -330,8 +344,8 @@ public void removeHistoryViewListener(@NonNull HistoryViewDelegate listener) { public void switchBookmarks() { if (isHistoryVisible()) { - hideHistory(); - showBookmarks(); + hideHistory(false); + showBookmarks(false); } else if (isBookmarksVisible()) { hideBookmarks(); @@ -342,8 +356,12 @@ public void switchBookmarks() { } public void showBookmarks() { + showBookmarks(true); + } + + public void showBookmarks(boolean switchSurface) { if (mView == null) { - setView(mBookmarksView); + setView(mBookmarksView, switchSurface); for (BookmarksViewDelegate listener : mBookmarksViewListeners) { listener.onBookmarksShown(this); } @@ -354,8 +372,12 @@ public void showBookmarks() { } public void hideBookmarks() { + hideBookmarks(true); + } + + public void hideBookmarks(boolean switchSurface) { if (mView != null) { - unsetView(mBookmarksView); + unsetView(mBookmarksView, switchSurface); for (BookmarksViewDelegate listener : mBookmarksViewListeners) { listener.onBookmarksHidden(this); } @@ -365,8 +387,8 @@ public void hideBookmarks() { public void switchHistory() { if (isBookmarksVisible()) { - hideBookmarks(); - showHistory(); + hideBookmarks(false); + showHistory(false); } else if (isHistoryVisible()) { hideHistory(); @@ -377,8 +399,12 @@ public void switchHistory() { } public void showHistory() { + showHistory(true); + } + + public void showHistory(boolean switchSurface) { if (mView == null) { - setView(mHistoryView); + setView(mHistoryView, switchSurface); for (HistoryViewDelegate listener : mHistoryViewListeners) { listener.onHistoryViewShown(this); } @@ -387,8 +413,12 @@ public void showHistory() { } public void hideHistory() { + hideHistory(true); + } + + public void hideHistory(boolean switchSurface) { if (mView != null) { - unsetView(mHistoryView); + unsetView(mHistoryView, switchSurface); for (HistoryViewDelegate listener : mHistoryViewListeners) { listener.onHistoryViewHidden(this); } @@ -1066,66 +1096,95 @@ private int getWindowWidth(float aWorldWidth) { return (int) Math.floor(SettingsStore.WINDOW_WIDTH_DEFAULT * aWorldWidth / WidgetPlacement.floatDimension(getContext(), R.dimen.window_world_width)); } - private HistoryCallback mHistoryCallback = new HistoryCallback() { - @Override - public void onClearHistory(View view) { - view.requestFocusFromTouch(); - showClearCacheDialog(); + private void showLibraryItemContextMenu(@NotNull View view, LibraryItemContextMenu.LibraryContextMenuItem item, boolean isLastVisibleItem) { + view.requestFocusFromTouch(); + + if (mLibraryItemContextMenu != null) { + mLibraryItemContextMenu.hide(REMOVE_WIDGET); } - @Override - public void onShowContextMenu(View view, VisitInfo item, boolean isLastVisibleItem) { - view.requestFocusFromTouch(); + float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), WindowWidget.this); - if (mHistoryContextMenu != null) { - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + Rect offsetViewBounds = new Rect(); + getDrawingRect(offsetViewBounds); + offsetDescendantRectToMyCoords(view, offsetViewBounds); - float ratio = WidgetPlacement.viewToWidgetRatio(getContext(), WindowWidget.this); + mLibraryItemContextMenu = new LibraryItemContextMenuWidget(getContext()); + mLibraryItemContextMenu.mWidgetPlacement.parentHandle = getHandle(); - Rect offsetViewBounds = new Rect(); - getDrawingRect(offsetViewBounds); - offsetDescendantRectToMyCoords(view, offsetViewBounds); + PointF position; + if (isLastVisibleItem) { + mLibraryItemContextMenu.mWidgetPlacement.anchorY = 0.0f; + position = new PointF( + (offsetViewBounds.left + view.getWidth()) * ratio, + -(offsetViewBounds.top) * ratio); - mHistoryContextMenu = new HistoryItemContextMenuWidget(getContext()); - mHistoryContextMenu.mWidgetPlacement.parentHandle = getHandle(); + } else { + mLibraryItemContextMenu.mWidgetPlacement.anchorY = 1.0f; + position = new PointF( + (offsetViewBounds.left + view.getWidth()) * ratio, + -(offsetViewBounds.top + view.getHeight()) * ratio); + } - PointF position; - if (isLastVisibleItem) { - mHistoryContextMenu.mWidgetPlacement.anchorY = 0.0f; - position = new PointF( - (offsetViewBounds.left + view.getWidth()) * ratio, - -(offsetViewBounds.top) * ratio); + mLibraryItemContextMenu.setPosition(position); + mLibraryItemContextMenu.setItem(item); + mLibraryItemContextMenu.setHistoryContextMenuItemCallback((new LibraryItemContextMenuClickCallback() { + @Override + public void onOpenInNewWindowClick(LibraryItemContextMenu.LibraryContextMenuItem item) { + mWidgetManager.openNewWindow(item.getUrl()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); + } - } else { - mHistoryContextMenu.mWidgetPlacement.anchorY = 1.0f; - position = new PointF( - (offsetViewBounds.left + view.getWidth()) * ratio, - -(offsetViewBounds.top + view.getHeight()) * ratio); + @Override + public void onAddToBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item) { + SessionStore.get().getBookmarkStore().addBookmark(item.getUrl(), item.getTitle()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); } - mHistoryContextMenu.setPosition(position); - mHistoryContextMenu.setItem(item); - mHistoryContextMenu.setHistoryContextMenuItemCallback((new HistoryItemContextMenuClickCallback() { - @Override - public void onOpenInNewWindowClick(VisitInfo item) { - mWidgetManager.openNewWindow(item.getUrl()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + @Override + public void onRemoveFromBookmarks(LibraryItemContextMenu.LibraryContextMenuItem item) { + SessionStore.get().getBookmarkStore().deleteBookmarkByURL(item.getUrl()); + mLibraryItemContextMenu.hide(REMOVE_WIDGET); + } + })); + mLibraryItemContextMenu.show(REQUEST_FOCUS); + } - @Override - public void onAddToBookmarks(VisitInfo item) { - SessionStore.get().getBookmarkStore().addBookmark(item.getUrl(), item.getTitle()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } + private BookmarksCallback mBookmarksCallback = new BookmarksCallback() { - @Override - public void onRemoveFromBookmarks(VisitInfo item) { - SessionStore.get().getBookmarkStore().deleteBookmarkByURL(item.getUrl()); - mHistoryContextMenu.hide(REMOVE_WIDGET); - } - })); - mHistoryContextMenu.show(REQUEST_FOCUS); + @Override + public void onClearBookmarks(View view) { + // Not used ATM + } + + @Override + public void onShowContextMenu(@NonNull View view, @NotNull BookmarkNode item, boolean isLastVisibleItem) { + showLibraryItemContextMenu( + view, + new LibraryItemContextMenu.LibraryContextMenuItem( + item.getUrl(), + item.getTitle(), + LibraryItemContextMenu.LibraryItemType.BOOKMARKS), + isLastVisibleItem); + } + }; + + private HistoryCallback mHistoryCallback = new HistoryCallback() { + @Override + public void onClearHistory(@NonNull View view) { + view.requestFocusFromTouch(); + showClearCacheDialog(); + } + + @Override + public void onShowContextMenu(@NonNull View view, @NonNull VisitInfo item, boolean isLastVisibleItem) { + showLibraryItemContextMenu( + view, + new LibraryItemContextMenu.LibraryContextMenuItem( + item.getUrl(), + item.getTitle(), + LibraryItemContextMenu.LibraryItemType.HISTORY), + isLastVisibleItem); } }; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java similarity index 77% rename from app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java rename to app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java index f32d6f992..9de3aac87 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/HistoryItemContextMenuWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/dialogs/LibraryItemContextMenuWidget.java @@ -14,39 +14,38 @@ import androidx.annotation.NonNull; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.ui.callbacks.HistoryItemContextMenuClickCallback; -import org.mozilla.vrbrowser.ui.views.HistoryItemContextMenu; +import org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback; +import org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu; import org.mozilla.vrbrowser.ui.widgets.UIWidget; import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.ViewUtils; -import mozilla.components.concept.storage.VisitInfo; +public class LibraryItemContextMenuWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { -public class HistoryItemContextMenuWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { - - private HistoryItemContextMenu mContextMenu; + private LibraryItemContextMenu mContextMenu; private int mMaxHeight; private PointF mTranslation; + private int height; - public HistoryItemContextMenuWidget(Context aContext) { + public LibraryItemContextMenuWidget(Context aContext) { super(aContext); - mContextMenu = new HistoryItemContextMenu(aContext); + mContextMenu = new LibraryItemContextMenu(aContext); initialize(); } - public HistoryItemContextMenuWidget(Context aContext, AttributeSet aAttrs) { + public LibraryItemContextMenuWidget(Context aContext, AttributeSet aAttrs) { super(aContext, aAttrs); - mContextMenu = new HistoryItemContextMenu(aContext, aAttrs); + mContextMenu = new LibraryItemContextMenu(aContext, aAttrs); initialize(); } - public HistoryItemContextMenuWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + public LibraryItemContextMenuWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { super(aContext, aAttrs, aDefStyle); - mContextMenu = new HistoryItemContextMenu(aContext, aAttrs, aDefStyle); + mContextMenu = new LibraryItemContextMenu(aContext, aAttrs, aDefStyle); initialize(); } @@ -71,7 +70,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void show(@ShowFlags int aShowFlags) { - mWidgetManager.addFocusChangeListener(HistoryItemContextMenuWidget.this); + mWidgetManager.addFocusChangeListener(LibraryItemContextMenuWidget.this); measure(View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); @@ -89,9 +88,9 @@ public void onGlobalLayout() { mWidgetPlacement.translationX = mTranslation.x - (getWidth()/mWidgetPlacement.density); mWidgetPlacement.translationY = mTranslation.y + getResources().getDimension(R.dimen.library_context_menu_top_margin)/mWidgetPlacement.density; - mWidgetPlacement.width = (int)(getWidth()/mWidgetPlacement.density); - mWidgetPlacement.height = (int)(getHeight()/mWidgetPlacement.density); - mWidgetManager.updateWidget(HistoryItemContextMenuWidget.this); + mWidgetPlacement.width = (int)((getWidth() + 5)/mWidgetPlacement.density); + mWidgetPlacement.height = mContextMenu.getMenuHeight() + 5; + mWidgetManager.updateWidget(LibraryItemContextMenuWidget.this); } }); } @@ -109,11 +108,11 @@ protected void onDismiss() { hide(REMOVE_WIDGET); } - public void setHistoryContextMenuItemCallback(HistoryItemContextMenuClickCallback callback) { + public void setHistoryContextMenuItemCallback(LibraryItemContextMenuClickCallback callback) { mContextMenu.setContextMenuClickCallback(callback); } - public void setItem(@NonNull VisitInfo item) { + public void setItem(@NonNull LibraryItemContextMenu.LibraryContextMenuItem item) { mContextMenu.setItem(item); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java index fefd683f2..c5f83f406 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/AnimationHelper.java @@ -1,6 +1,7 @@ package org.mozilla.vrbrowser.utils; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.util.Log; import android.view.View; @@ -10,6 +11,10 @@ import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; +import androidx.annotation.NonNull; + +import org.mozilla.gecko.util.ThreadUtils; + public class AnimationHelper { public static final long FADE_ANIMATION_DURATION = 150; @@ -110,4 +115,30 @@ public void onAnimationRepeat(Animator animator) { }); animation.start(); } + + public static void scaleIn(@NonNull View aView, long duration, long delay, final Runnable aCallback) { + aView.setScaleX(0); + aView.setScaleY(0); + aView.animate().setStartDelay(delay).scaleX(1f).scaleY(1f).setDuration(duration).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (aCallback != null) + ThreadUtils.postToUiThread(aCallback); + } + }).setUpdateListener(animation -> aView.invalidate()); + } + + public static void scaleOut(@NonNull View aView, long duration, long delay, final Runnable aCallback) { + aView.setScaleX(1); + aView.setScaleY(1); + aView.animate().setStartDelay(delay).scaleX(0f).scaleY(0f).setDuration(duration).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (aCallback != null) + ThreadUtils.postToUiThread(aCallback); + } + }).setUpdateListener(animation -> aView.invalidate()); + } } diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_bottom.xml b/app/src/main/res/drawable/library_context_menu_item_background_bottom.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_bottom.xml rename to app/src/main/res/drawable/library_context_menu_item_background_bottom.xml diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_middle.xml b/app/src/main/res/drawable/library_context_menu_item_background_middle.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_middle.xml rename to app/src/main/res/drawable/library_context_menu_item_background_middle.xml diff --git a/app/src/main/res/drawable/library_context_menu_item_background_single.xml b/app/src/main/res/drawable/library_context_menu_item_background_single.xml new file mode 100644 index 000000000..7c243ee02 --- /dev/null +++ b/app/src/main/res/drawable/library_context_menu_item_background_single.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/library_panel_context_menu_item_background_color_top.xml b/app/src/main/res/drawable/library_context_menu_item_background_top.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_context_menu_item_background_color_top.xml rename to app/src/main/res/drawable/library_context_menu_item_background_top.xml diff --git a/app/src/main/res/drawable/library_panel_item_background_color.xml b/app/src/main/res/drawable/library_item_background_color.xml similarity index 100% rename from app/src/main/res/drawable/library_panel_item_background_color.xml rename to app/src/main/res/drawable/library_item_background_color.xml diff --git a/app/src/main/res/drawable/library_notification_background.xml b/app/src/main/res/drawable/library_notification_background.xml new file mode 100644 index 000000000..fe67ba0a1 --- /dev/null +++ b/app/src/main/res/drawable/library_notification_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/library_notification_background_triangle.xml b/app/src/main/res/drawable/library_notification_background_triangle.xml new file mode 100644 index 000000000..4d409349a --- /dev/null +++ b/app/src/main/res/drawable/library_notification_background_triangle.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/main_button_icon_color_notification.xml b/app/src/main/res/drawable/main_button_icon_color_notification.xml new file mode 100644 index 000000000..c9847983e --- /dev/null +++ b/app/src/main/res/drawable/main_button_icon_color_notification.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/bookmark_item.xml b/app/src/main/res/layout/bookmark_item.xml index 2ad2b3872..708497091 100644 --- a/app/src/main/res/layout/bookmark_item.xml +++ b/app/src/main/res/layout/bookmark_item.xml @@ -1,73 +1,133 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + type="org.mozilla.vrbrowser.ui.callbacks.BookmarkItemCallback" /> + + - - - + android:layout_height="@dimen/library_item_row_height" + android:background="@color/void_color"> - + android:layout_height="match_parent" + android:background="@drawable/library_item_background_color" + android:clickable="true" + android:descendantFocusability="blocksDescendants" + android:focusable="true" + android:gravity="center_vertical" + android:onClick="@{(view) -> callback.onClick(view, item)}" + android:paddingStart="10dp" + android:paddingEnd="10dp" + android:soundEffectsEnabled="false"> + + + + + + + + + + + + + - - + + + + + diff --git a/app/src/main/res/layout/bookmarks.xml b/app/src/main/res/layout/bookmarks.xml index 62300df87..514cff383 100644 --- a/app/src/main/res/layout/bookmarks.xml +++ b/app/src/main/res/layout/bookmarks.xml @@ -1,5 +1,6 @@ - @@ -11,6 +12,10 @@ + + + android:paddingStart="30dp" + android:paddingTop="30dp" + android:paddingEnd="30dp" + android:paddingBottom="30dp"> + app:visibleGone="@{!isLoading && isEmpty}"> + + - + android:layout_marginEnd="20dp" + android:layout_marginBottom="20dp" + app:visibleGone="@{!isLoading && !isEmpty}"> + + + + + @@ -47,7 +47,7 @@ android:layout_height="wrap_content" android:layout_marginTop="35dp" android:fontFamily="sans-serif" - android:text="@string/history_title" + android:text="@string/history_empty" android:textAlignment="center" android:textAllCaps="false" android:textColor="@color/fog" diff --git a/app/src/main/res/layout/history_item.xml b/app/src/main/res/layout/history_item.xml index 863a1af66..55d7b8753 100644 --- a/app/src/main/res/layout/history_item.xml +++ b/app/src/main/res/layout/history_item.xml @@ -21,7 +21,7 @@ + tools:text="Item Title" /> @@ -94,7 +94,7 @@ android:singleLine="true" android:text="@{item.url}" android:textColor="@color/library_panel_description_color" - android:textSize="@dimen/history_url_text_size" + android:textSize="@dimen/library_item_url_text_size" tools:text="http://mozilla.org" /> @@ -116,7 +116,7 @@ android:scrollHorizontally="true" android:singleLine="true" android:textColor="@color/library_panel_description_color" - android:textSize="@dimen/history_date_text_size" + android:textSize="@dimen/library_item_date_text_size" app:bindDate="@{item.visitTime}" app:visibleInvisible="@{!isHovered}" tools:text="8/2/19, 2:18 PM" /> diff --git a/app/src/main/res/layout/history_item_context_menu.xml b/app/src/main/res/layout/library_item_context_menu.xml similarity index 81% rename from app/src/main/res/layout/history_item_context_menu.xml rename to app/src/main/res/layout/library_item_context_menu.xml index c5fafc246..f61f2eb83 100644 --- a/app/src/main/res/layout/history_item_context_menu.xml +++ b/app/src/main/res/layout/library_item_context_menu.xml @@ -3,13 +3,15 @@ xmlns:tools="http://schemas.android.com/tools"> + + + type="org.mozilla.vrbrowser.ui.callbacks.LibraryItemContextMenuClickCallback" /> + type="org.mozilla.vrbrowser.ui.views.LibraryItemContextMenu.LibraryContextMenuItem" /> diff --git a/app/src/main/res/layout/library_notification.xml b/app/src/main/res/layout/library_notification.xml new file mode 100644 index 000000000..e6a2302da --- /dev/null +++ b/app/src/main/res/layout/library_notification.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 8cc0bee6e..1c159db98 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -18,6 +18,7 @@ + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 3e800275b..6e9475779 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -127,7 +127,7 @@ 1.2 206dp 40dp - 4.0 + 4.5 585dp @@ -197,15 +197,15 @@ 2dp 0dp - - 64dp - - - 38dp - 18sp - 20sp - 16sp - 16sp + + 55dp + 18sp + 20sp + 16sp + 16sp + 2dp + 0dp + 13sp 22dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f34dedcbc..0abf75002 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -636,6 +636,12 @@ Bookmarks + + Your Bookmarks List is Empty + + + Click the @ icon when you’re on a page to add it to your bookmarks. + Loading Bookmarks @@ -644,6 +650,10 @@ item listed in the Bookmarks List. --> Remove Bookmark + + Saved to Bookmarks! + @@ -666,6 +676,9 @@ History + + Your History is Empty + You can access your browsing history here diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 761c2e2ed..d4a57879b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -65,6 +65,7 @@ @drawable/main_button_icon_color @drawable/main_button_icon_color_private @drawable/main_button_icon_color_active + @drawable/main_button_icon_color_notification