Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for apps supporting PROCESS_TEXT intents. #3720

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Expand Up @@ -233,4 +233,14 @@

</application>

<!-- Starting with api level 30, we need to declare intent actions inside a queries element,
to be able to find activities which support a given intent
https://developer.android.com/training/package-visibility/use-cases#custom-text-selection-actions -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>

</manifest>
22 changes: 21 additions & 1 deletion app/src/main/java/com/termux/app/TermuxActivity.java
Expand Up @@ -18,6 +18,7 @@
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
Expand All @@ -39,6 +40,7 @@
import com.termux.shared.data.IntentUtils;
import com.termux.shared.android.PermissionUtils;
import com.termux.shared.data.DataUtils;
import com.termux.shared.interact.ProcessTextUtils;
import com.termux.shared.termux.TermuxConstants;
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY;
import com.termux.app.activities.HelpActivity;
Expand Down Expand Up @@ -68,6 +70,7 @@
import androidx.viewpager.widget.ViewPager;

import java.util.Arrays;
import java.util.List;

/**
* A terminal emulator activity.
Expand Down Expand Up @@ -189,6 +192,8 @@ public final class TermuxActivity extends AppCompatActivity implements ServiceCo
private static final int CONTEXT_MENU_HELP_ID = 7;
private static final int CONTEXT_MENU_SETTINGS_ID = 8;
private static final int CONTEXT_MENU_REPORT_ID = 9;
private static final int CONTEXT_MENU_SUBMENU_PROCESS_TEXT_ID = 11;
private static final int CONTEXT_MENU_LAUNCH_EXTERNAL_APP_PROCESS_TEXT_ID = 12;

private static final String ARG_TERMINAL_TOOLBAR_TEXT_INPUT = "terminal_toolbar_text_input";
private static final String ARG_ACTIVITY_RECREATED = "activity_recreated";
Expand Down Expand Up @@ -642,8 +647,20 @@ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuIn

menu.add(Menu.NONE, CONTEXT_MENU_SELECT_URL_ID, Menu.NONE, R.string.action_select_url);
menu.add(Menu.NONE, CONTEXT_MENU_SHARE_TRANSCRIPT_ID, Menu.NONE, R.string.action_share_transcript);
if (!DataUtils.isNullOrEmpty(mTerminalView.getStoredSelectedText()))
String selectedText = mTerminalView.getStoredSelectedText();
if (!DataUtils.isNullOrEmpty(selectedText)) {
menu.add(Menu.NONE, CONTEXT_MENU_SHARE_SELECTED_TEXT, Menu.NONE, R.string.action_share_selected_text);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
List<ProcessTextUtils.ProcessTextActivityData> processTextActivities = ProcessTextUtils.getProcessTextActivities(this, selectedText);
if (!processTextActivities.isEmpty()) {
SubMenu processTextSubmenu = menu.addSubMenu(Menu.NONE, CONTEXT_MENU_SUBMENU_PROCESS_TEXT_ID, Menu.NONE, R.string.action_process_text);
for (ProcessTextUtils.ProcessTextActivityData processTextActivityData : processTextActivities) {
processTextSubmenu.add(Menu.NONE, CONTEXT_MENU_LAUNCH_EXTERNAL_APP_PROCESS_TEXT_ID, Menu.NONE, processTextActivityData.label)
.setIntent(processTextActivityData.intent);
}
}
}
}
if (addAutoFillMenu)
menu.add(Menu.NONE, CONTEXT_MENU_AUTOFILL_ID, Menu.NONE, R.string.action_autofill_password);
menu.add(Menu.NONE, CONTEXT_MENU_RESET_TERMINAL_ID, Menu.NONE, R.string.action_reset_terminal);
Expand Down Expand Up @@ -700,6 +717,9 @@ public boolean onContextItemSelected(MenuItem item) {
case CONTEXT_MENU_REPORT_ID:
mTermuxTerminalViewClient.reportIssueFromTranscript();
return true;
case CONTEXT_MENU_LAUNCH_EXTERNAL_APP_PROCESS_TEXT_ID:
ActivityUtils.startActivity(this, item.getIntent());
return true;
default:
return super.onContextItemSelected(item);
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Expand Up @@ -90,6 +90,8 @@
<string name="msg_generating_report">Generating Report</string>
<string name="msg_add_termux_debug_info">Add termux debug info to report?</string>

<string name="action_process_text">Select text processing app…</string>

<string name="error_styling_not_installed">The &TERMUX_STYLING_APP_NAME; Plugin App is not installed.</string>
<string name="action_styling_install">Install</string>

Expand Down
@@ -0,0 +1,69 @@
package com.termux.shared.interact;

import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;

import java.util.ArrayList;
import java.util.List;

public class ProcessTextUtils {

/**
* Data required to interact with an activity that supports the PROCESS_TEXT intent.
*/
public static class ProcessTextActivityData {
/**
* The label of the external app activity.
*/
public final CharSequence label;

/**
* The intent to launch the activity.
*/
public final Intent intent;

private ProcessTextActivityData(CharSequence label, Intent intent) {
this.label = label;
this.intent = intent;
}
}

/**
* Retrieve a list of activities (including outside of this app) that support the
* {@link Intent#ACTION_PROCESS_TEXT} intent action. For each item in the returned
* list, its Intent can be used to launch the activity with the given text.
*
* @param context The context for operations
* @param text The text to send in the Intents for the supported activities.
* @return a List of {@link ProcessTextActivityData} for supported activities.
*/
@TargetApi(Build.VERSION_CODES.M)
public static List<ProcessTextActivityData> getProcessTextActivities(Context context, String text) {
PackageManager packageManager = context.getPackageManager();
Intent processTextIntent = new Intent(Intent.ACTION_PROCESS_TEXT);
processTextIntent.setType("text/plain");
List<ResolveInfo> activities = packageManager.queryIntentActivities(processTextIntent, PackageManager.MATCH_ALL);
List<ProcessTextActivityData> result = new ArrayList<>();
for (ResolveInfo resolveInfo : activities) {
CharSequence label = resolveInfo.loadLabel(packageManager);
result.add(new ProcessTextActivityData(label, createProcessTextIntentForPackage(resolveInfo.activityInfo, text)));
}
return result;
}

@TargetApi(Build.VERSION_CODES.M)
private static Intent createProcessTextIntentForPackage(ActivityInfo activityInfo, String text) {
Intent intent = new Intent(Intent.ACTION_PROCESS_TEXT);
intent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_PROCESS_TEXT, text);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}