diff --git a/app/src/main/java/com/cliqz/browser/main/TabFragment2.java b/app/src/main/java/com/cliqz/browser/main/TabFragment2.java index 4fe8dc9d5..b71ebb6b5 100644 --- a/app/src/main/java/com/cliqz/browser/main/TabFragment2.java +++ b/app/src/main/java/com/cliqz/browser/main/TabFragment2.java @@ -17,6 +17,7 @@ import android.util.Patterns; import android.util.TypedValue; import android.view.ContextThemeWrapper; +import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -25,10 +26,13 @@ import android.view.animation.Animation; import android.view.inputmethod.InputMethodManager; import android.webkit.WebView; +import android.widget.Button; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import android.widget.Toast; import androidx.annotation.ColorInt; @@ -51,6 +55,7 @@ import com.cliqz.browser.tabs.TabsManager; import com.cliqz.browser.telemetry.TelemetryKeys; import com.cliqz.browser.utils.AppBackgroundManager; +import com.cliqz.browser.utils.ClipboardHandler; import com.cliqz.browser.utils.ConfirmSubscriptionDialog; import com.cliqz.browser.utils.EnableNotificationDialog; import com.cliqz.browser.utils.SubscriptionsManager; @@ -286,6 +291,19 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat mUnbinder = ButterKnife.bind(this, view); searchBar.setSearchEditText(searchEditText); searchBar.setProgressBar(progressBar); + searchBar.setOnClickListener(v -> { + if (searchBar.titleBar.getVisibility() == View.VISIBLE) { + if (antiTrackingDetails != null) { + searchBar.setAntiTrackingDetailsVisibility(View.GONE); + } + searchBar.showSearchEditText(); + if (searchBar.mListener != null) { + searchBar.mListener.onTitleClicked(searchBar); + } + } + }); + searchBar.setOnLongClickListener(v -> showClipboardPopUp()); + final MainActivity activity = (MainActivity) getActivity(); final FlavoredActivityComponent component = activity != null ? BrowserApp.getActivityComponent(activity) : null; @@ -350,6 +368,60 @@ String getTelemetryView() { } } + private boolean showClipboardPopUp() { + final Context context = getContext(); + final ClipboardHandler clipboard = new ClipboardHandler(context); + final View customView = LayoutInflater.from(context) + .inflate(R.layout.searchbar_long_press_popup_window, null); + final PopupWindow popupWindow = new PopupWindow( + customView, + LinearLayout.LayoutParams.WRAP_CONTENT, + context.getResources().getDimensionPixelSize(R.dimen.context_menu_height), + true + ); + + popupWindow.setElevation(context.getResources().getDimension(R.dimen.context_menu_elevation)); + + final Button copyButton = customView.findViewById(R.id.copy); + final Button pasteButton = customView.findViewById(R.id.paste); + final Button pasteAndGoButton = customView.findViewById(R.id.paste_and_go); + + copyButton.setVisibility(searchBar.titleBar.getText().toString().isEmpty() ? View.GONE : View.VISIBLE); + pasteButton.setVisibility((clipboard.getText() != null && !clipboard.getText().isEmpty()) ? View.VISIBLE : View.GONE); + pasteAndGoButton.setVisibility((clipboard.getText() != null && !clipboard.getText().isEmpty()) ? View.VISIBLE : View.GONE); + + copyButton.setOnClickListener(v -> { + popupWindow.dismiss(); + clipboard.setText(searchBar.titleBar.getText().toString()); + Toast.makeText( + context, + R.string.search_bar_long_press_popup_copied_to_clipboard_toast, + Toast.LENGTH_SHORT + ).show(); + }); + + pasteButton.setOnClickListener(v -> { + popupWindow.dismiss(); + searchBar.setSearchText(clipboard.getText()); + searchBar.showSearchEditText(); + }); + + pasteAndGoButton.setOnClickListener(v -> { + popupWindow.dismiss(); + searchBar.setSearchText(clipboard.getText()); + final String content = searchBar.getSearchText(); + searchOrVisitUrl(content); + }); + + popupWindow.showAsDropDown( + toolBarContainer, + getContext().getResources().getDimensionPixelSize(R.dimen.context_menu_x_offset), + getContext().getResources().getDimensionPixelSize(R.dimen.context_menu_y_offset), + Gravity.START + ); + return true; + } + private void updateVpnIcon() { if (mVpnPanelButton != null) { if (vpnHandler.isVpnConnected()) { @@ -605,30 +677,36 @@ boolean onEditorAction(EditText editText, int actionId, KeyEvent keyEvent) { if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_DOWN) { final String content = searchBar.getSearchText(); - if (content != null && !content.isEmpty()) { - final Object event; - if (Patterns.WEB_URL.matcher(content).matches()) { - final String guessedUrl = StringUtils.guessUrl(content); - if (searchBar.isAutoCompleted()) { - telemetry.sendResultEnterSignal(false, true, - searchBar.getQuery().length(), guessedUrl.length()); - } else { - telemetry.sendResultEnterSignal(false, false, content.length(), -1); - } - event = CliqzMessages.OpenLink.open(guessedUrl); - } else { - telemetry.sendResultEnterSignal(true, false, content.length(), -1); - setSearchEngine(); - String searchUrl = mSearchEngine + UrlUtils.QUERY_PLACE_HOLDER; - event = CliqzMessages.OpenLink.open(UrlUtils.smartUrlFilter(content, true, searchUrl)); - } - if (!onBoardingHelper.conditionallyShowSearchDescription()) { - bus.post(event); + return searchOrVisitUrl(content); + } + return false; + } + + + private boolean searchOrVisitUrl(String content) { + if (content != null && !content.isEmpty()) { + final Object event; + if (Patterns.WEB_URL.matcher(content).matches()) { + final String guessedUrl = StringUtils.guessUrl(content); + if (searchBar.isAutoCompleted()) { + telemetry.sendResultEnterSignal(false, true, + searchBar.getQuery().length(), guessedUrl.length()); } else { - hideKeyboard(null); + telemetry.sendResultEnterSignal(false, false, content.length(), -1); } - return true; + event = CliqzMessages.OpenLink.open(guessedUrl); + } else { + telemetry.sendResultEnterSignal(true, false, content.length(), -1); + setSearchEngine(); + final String searchUrl = mSearchEngine + UrlUtils.QUERY_PLACE_HOLDER; + event = CliqzMessages.OpenLink.open(UrlUtils.smartUrlFilter(content, true, searchUrl)); + } + if (!onBoardingHelper.conditionallyShowSearchDescription()) { + bus.post(event); + } else { + hideKeyboard(null); } + return true; } return false; } diff --git a/app/src/main/java/com/cliqz/browser/utils/ClipboardHandler.kt b/app/src/main/java/com/cliqz/browser/utils/ClipboardHandler.kt new file mode 100644 index 000000000..0907d1fff --- /dev/null +++ b/app/src/main/java/com/cliqz/browser/utils/ClipboardHandler.kt @@ -0,0 +1,46 @@ +/* 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 com.cliqz.browser.utils + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context + +private const val MIME_TYPE_TEXT_PLAIN = "text/plain" +private const val MIME_TYPE_TEXT_HTML = "text/html" + +/** + * A clipboard utility class that allows copying and pasting links/text to & from the clipboard + */ +class ClipboardHandler(context: Context) { + private val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + + var text: String? + get() { + if (!clipboard.isPrimaryClipEmpty() && + (clipboard.isPrimaryClipPlainText() || + clipboard.isPrimaryClipHtmlText()) + ) { + return clipboard.firstPrimaryClipItem?.text.toString() + } + return null + } + set(value) { + clipboard.primaryClip = ClipData.newPlainText("Text", value) + } + + val url: String? = text + + private fun ClipboardManager.isPrimaryClipPlainText() = + primaryClipDescription?.hasMimeType(MIME_TYPE_TEXT_PLAIN) ?: false + + private fun ClipboardManager.isPrimaryClipHtmlText() = + primaryClipDescription?.hasMimeType(MIME_TYPE_TEXT_HTML) ?: false + + private fun ClipboardManager.isPrimaryClipEmpty() = primaryClip?.itemCount == 0 + + private val ClipboardManager.firstPrimaryClipItem: ClipData.Item? + get() = primaryClip?.getItemAt(0) +} diff --git a/app/src/main/java/com/cliqz/browser/widget/SearchBar.java b/app/src/main/java/com/cliqz/browser/widget/SearchBar.java index 9006d9651..682c43ac8 100644 --- a/app/src/main/java/com/cliqz/browser/widget/SearchBar.java +++ b/app/src/main/java/com/cliqz/browser/widget/SearchBar.java @@ -11,7 +11,6 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -73,7 +72,7 @@ public interface Listener extends TextWatcher, OnFocusChangeListener { AppCompatImageView lock; @BindView(R.id.title_bar) - TextView titleBar; + public TextView titleBar; @Nullable @BindView(R.id.tracker_counter) @@ -84,7 +83,7 @@ public interface Listener extends TextWatcher, OnFocusChangeListener { ViewGroup antiTrackingDetails; @Nullable - Listener mListener; + public Listener mListener; @Nullable @BindView(R.id.reader_mode_button) @@ -327,35 +326,6 @@ public void showKeyBoard() { } } - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if (event.getAction() != MotionEvent.ACTION_DOWN) { - return super.onInterceptTouchEvent(event); - } - - if (antiTrackingDetails != null && antiTrackingDetails.getVisibility() == VISIBLE - && event.getX() >= antiTrackingDetails.getX()) { - return super.onInterceptTouchEvent(event); - } - - if (readerModeButton != null && readerModeButton.getVisibility() == VISIBLE - && event.getX() >= readerModeButton.getX()) { - return super.onInterceptTouchEvent(event); - } - - if (titleBar.getVisibility() == VISIBLE) { - if (antiTrackingDetails != null) { - setAntiTrackingDetailsVisibility(GONE); - } - showSearchEditText(); - if (mListener != null) { - mListener.onTitleClicked(SearchBar.this); - } - return true; - } - return super.onInterceptTouchEvent(event); - } - /** * Updates the color of the search bar depending on the mode of the tab * diff --git a/app/src/main/res/drawable/rounded_all_corners.xml b/app/src/main/res/drawable/rounded_all_corners.xml new file mode 100644 index 000000000..cc4bfd9ca --- /dev/null +++ b/app/src/main/res/drawable/rounded_all_corners.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/searchbar_long_press_popup_window.xml b/app/src/main/res/layout/searchbar_long_press_popup_window.xml new file mode 100644 index 000000000..4e52f14f5 --- /dev/null +++ b/app/src/main/res/layout/searchbar_long_press_popup_window.xml @@ -0,0 +1,51 @@ + + + +