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 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 49887eaea..37a926027 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -37,4 +37,12 @@
10dp
5dp
24sp
+
+ 6dp
+
+ 36dp
+ 13dp
+ 8dp
+
+ 3dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c6e2f98cf..047b7e3af 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -329,4 +329,10 @@ Now you are ready to send tabs between your desktop and your mobile device. You
Build Date & Time
Enable Android Auto Backup
Auto Backup for Apps automatically backs up a user\'s data by uploading it to the user\'s Google Drive
+
+
+ Copy
+ Paste & Go
+ Paste
+ URL copied to clipboard