termux-app
278 строк · 10.2 Кб
1package com.termux.shared.activities;
2
3import android.annotation.SuppressLint;
4import android.app.Activity;
5import android.content.Context;
6import android.content.Intent;
7import android.graphics.Typeface;
8import android.os.Bundle;
9import android.text.Editable;
10import android.text.InputFilter;
11import android.text.TextWatcher;
12import android.view.Menu;
13import android.view.MenuInflater;
14import android.view.MenuItem;
15import android.view.View;
16import android.view.ViewGroup;
17import android.widget.EditText;
18import android.widget.HorizontalScrollView;
19import android.widget.LinearLayout;
20import android.widget.TextView;
21
22import androidx.annotation.NonNull;
23import androidx.appcompat.app.ActionBar;
24import androidx.appcompat.app.AppCompatActivity;
25import androidx.appcompat.widget.Toolbar;
26
27import com.termux.shared.interact.ShareUtils;
28import com.termux.shared.logger.Logger;
29import com.termux.shared.R;
30import com.termux.shared.models.TextIOInfo;
31import com.termux.shared.view.KeyboardUtils;
32
33import org.jetbrains.annotations.NotNull;
34
35import java.util.Locale;
36
37/**
38* An activity to edit or view text based on config passed as {@link TextIOInfo}.
39*
40* Add Following to `AndroidManifest.xml` to use in an app:
41*
42* {@code ` <activity android:name="com.termux.shared.activities.TextIOActivity" android:theme="@style/Theme.AppCompat.TermuxTextIOActivity" />` }
43*/
44public class TextIOActivity extends AppCompatActivity {
45
46private static final String CLASS_NAME = ReportActivity.class.getCanonicalName();
47public static final String EXTRA_TEXT_IO_INFO_OBJECT = CLASS_NAME + ".EXTRA_TEXT_IO_INFO_OBJECT";
48
49private TextView mTextIOLabel;
50private View mTextIOLabelSeparator;
51private EditText mTextIOText;
52private HorizontalScrollView mTextIOHorizontalScrollView;
53private LinearLayout mTextIOTextLinearLayout;
54private TextView mTextIOTextCharacterUsage;
55
56private TextIOInfo mTextIOInfo;
57private Bundle mBundle;
58
59private static final String LOG_TAG = "TextIOActivity";
60
61@Override
62protected void onCreate(Bundle savedInstanceState) {
63super.onCreate(savedInstanceState);
64Logger.logVerbose(LOG_TAG, "onCreate");
65
66setContentView(R.layout.activity_text_io);
67
68mTextIOLabel = findViewById(R.id.text_io_label);
69mTextIOLabelSeparator = findViewById(R.id.text_io_label_separator);
70mTextIOText = findViewById(R.id.text_io_text);
71mTextIOHorizontalScrollView = findViewById(R.id.text_io_horizontal_scroll_view);
72mTextIOTextLinearLayout = findViewById(R.id.text_io_text_linear_layout);
73mTextIOTextCharacterUsage = findViewById(R.id.text_io_text_character_usage);
74
75Toolbar toolbar = findViewById(R.id.toolbar);
76if (toolbar != null) {
77setSupportActionBar(toolbar);
78}
79
80mBundle = null;
81Intent intent = getIntent();
82if (intent != null)
83mBundle = intent.getExtras();
84else if (savedInstanceState != null)
85mBundle = savedInstanceState;
86
87updateUI();
88}
89
90@Override
91protected void onNewIntent(Intent intent) {
92super.onNewIntent(intent);
93Logger.logVerbose(LOG_TAG, "onNewIntent");
94
95// Views must be re-created since different configs for isEditingTextDisabled() and
96// isHorizontallyScrollable() will not work or at least reliably
97finish();
98startActivity(intent);
99}
100
101@SuppressLint("ClickableViewAccessibility")
102private void updateUI() {
103if (mBundle == null) {
104finish(); return;
105}
106
107mTextIOInfo = (TextIOInfo) mBundle.getSerializable(EXTRA_TEXT_IO_INFO_OBJECT);
108if (mTextIOInfo == null) {
109finish(); return;
110}
111
112final ActionBar actionBar = getSupportActionBar();
113if (actionBar != null) {
114if (mTextIOInfo.getTitle() != null)
115actionBar.setTitle(mTextIOInfo.getTitle());
116else
117actionBar.setTitle("Text Input");
118
119if (mTextIOInfo.shouldShowBackButtonInActionBar()) {
120actionBar.setDisplayHomeAsUpEnabled(true);
121actionBar.setDisplayShowHomeEnabled(true);
122}
123}
124
125mTextIOLabel.setVisibility(View.GONE);
126mTextIOLabelSeparator.setVisibility(View.GONE);
127if (mTextIOInfo.isLabelEnabled()) {
128mTextIOLabel.setVisibility(View.VISIBLE);
129mTextIOLabelSeparator.setVisibility(View.VISIBLE);
130mTextIOLabel.setText(mTextIOInfo.getLabel());
131mTextIOLabel.setFilters(new InputFilter[] { new InputFilter.LengthFilter(TextIOInfo.LABEL_SIZE_LIMIT_IN_BYTES) });
132mTextIOLabel.setTextSize(mTextIOInfo.getLabelSize());
133mTextIOLabel.setTextColor(mTextIOInfo.getLabelColor());
134mTextIOLabel.setTypeface(Typeface.create(mTextIOInfo.getLabelTypeFaceFamily(), mTextIOInfo.getLabelTypeFaceStyle()));
135}
136
137
138if (mTextIOInfo.isHorizontallyScrollable()) {
139mTextIOHorizontalScrollView.setEnabled(true);
140mTextIOText.setHorizontallyScrolling(true);
141} else {
142// Remove mTextIOHorizontalScrollView and add mTextIOText in its place
143ViewGroup parent = (ViewGroup) mTextIOHorizontalScrollView.getParent();
144if (parent != null && parent.indexOfChild(mTextIOText) < 0) {
145ViewGroup.LayoutParams params = mTextIOHorizontalScrollView.getLayoutParams();
146int index = parent.indexOfChild(mTextIOHorizontalScrollView);
147mTextIOTextLinearLayout.removeAllViews();
148mTextIOHorizontalScrollView.removeAllViews();
149parent.removeView(mTextIOHorizontalScrollView);
150parent.addView(mTextIOText, index, params);
151mTextIOText.setHorizontallyScrolling(false);
152}
153}
154
155mTextIOText.setText(mTextIOInfo.getText());
156mTextIOText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(mTextIOInfo.getTextLengthLimit()) });
157mTextIOText.setTextSize(mTextIOInfo.getTextSize());
158mTextIOText.setTextColor(mTextIOInfo.getTextColor());
159mTextIOText.setTypeface(Typeface.create(mTextIOInfo.getTextTypeFaceFamily(), mTextIOInfo.getTextTypeFaceStyle()));
160
161// setTextIsSelectable must be called after changing KeyListener to regain focusability and selectivity
162if (mTextIOInfo.isEditingTextDisabled()) {
163mTextIOText.setCursorVisible(false);
164mTextIOText.setKeyListener(null);
165mTextIOText.setTextIsSelectable(true);
166}
167
168if (mTextIOInfo.shouldShowTextCharacterUsage()) {
169mTextIOTextCharacterUsage.setVisibility(View.VISIBLE);
170updateTextIOTextCharacterUsage(mTextIOInfo.getText());
171
172mTextIOText.addTextChangedListener(new TextWatcher() {
173@Override
174public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
175@Override
176public void onTextChanged(CharSequence s, int start, int before, int count) {}
177@Override
178public void afterTextChanged(Editable editable) {
179if (editable != null)
180updateTextIOTextCharacterUsage(editable.toString());
181}
182});
183} else {
184mTextIOTextCharacterUsage.setVisibility(View.GONE);
185mTextIOText.addTextChangedListener(null);
186}
187}
188
189private void updateTextIOInfoText() {
190if (mTextIOText != null)
191mTextIOInfo.setText(mTextIOText.getText().toString());
192}
193
194private void updateTextIOTextCharacterUsage(String text) {
195if (text == null) text = "";
196if (mTextIOTextCharacterUsage != null)
197mTextIOTextCharacterUsage.setText(String.format(Locale.getDefault(), "%1$d/%2$d", text.length(), mTextIOInfo.getTextLengthLimit()));
198}
199
200@Override
201public void onSaveInstanceState(@NonNull Bundle outState) {
202super.onSaveInstanceState(outState);
203
204updateTextIOInfoText();
205outState.putSerializable(EXTRA_TEXT_IO_INFO_OBJECT, mTextIOInfo);
206}
207
208@Override
209public boolean onCreateOptionsMenu(final Menu menu) {
210final MenuInflater inflater = getMenuInflater();
211inflater.inflate(R.menu.menu_text_io, menu);
212return true;
213}
214
215@Override
216public boolean onOptionsItemSelected(final MenuItem item) {
217String text = "";
218if (mTextIOText != null)
219text = mTextIOText.getText().toString();
220
221int id = item.getItemId();
222if (id == android.R.id.home) {
223confirm();
224} if (id == R.id.menu_item_cancel) {
225cancel();
226} else if (id == R.id.menu_item_share_text) {
227ShareUtils.shareText(this, mTextIOInfo.getTitle(), text);
228} else if (id == R.id.menu_item_copy_text) {
229ShareUtils.copyTextToClipboard(this, text, null);
230}
231
232return false;
233}
234
235@Override
236public void onBackPressed() {
237confirm();
238}
239
240/** Confirm current text and send it back to calling {@link Activity}. */
241private void confirm() {
242updateTextIOInfoText();
243KeyboardUtils.hideSoftKeyboard(this, mTextIOText);
244setResult(Activity.RESULT_OK, getResultIntent());
245finish();
246}
247
248/** Cancel current text and notify calling {@link Activity}. */
249private void cancel() {
250KeyboardUtils.hideSoftKeyboard(this, mTextIOText);
251setResult(Activity.RESULT_CANCELED, getResultIntent());
252finish();
253}
254
255@NotNull
256private Intent getResultIntent() {
257Intent intent = new Intent();
258Bundle bundle = new Bundle();
259bundle.putSerializable(EXTRA_TEXT_IO_INFO_OBJECT, mTextIOInfo);
260intent.putExtras(bundle);
261return intent;
262}
263
264/**
265* Get the {@link Intent} that can be used to start the {@link TextIOActivity}.
266*
267* @param context The {@link Context} for operations.
268* @param textIOInfo The {@link TextIOInfo} containing info for the edit text.
269*/
270public static Intent newInstance(@NonNull final Context context, @NonNull final TextIOInfo textIOInfo) {
271Intent intent = new Intent(context, TextIOActivity.class);
272Bundle bundle = new Bundle();
273bundle.putSerializable(EXTRA_TEXT_IO_INFO_OBJECT, textIOInfo);
274intent.putExtras(bundle);
275return intent;
276}
277
278}
279