terminals = new ObservableArrayList<>();
15 | private SshServerDataSource sshServerDataSource;
16 |
17 | public TerminalManagerViewModel() {
18 | loadTerminals();
19 | }
20 |
21 | private void loadTerminals() {
22 | terminals.clear();
23 | terminals.add(new TerminalItem(null));//local terminal
24 | BaseApp context = BaseApp.get();
25 | sshServerDataSource = new SshServerDataSource(ATermDatabase.getInstance(context).sshServerDao(), BaseApp.get().getAppExecutors());
26 | sshServerDataSource.getSshServers()
27 | .observeForever(sshServers -> {
28 | if (sshServers == null) {
29 | return;
30 | }
31 | for (SshServer server : sshServers) {
32 | terminals.add(new TerminalItem(server));
33 | }
34 | });
35 | }
36 |
37 | public void addServer(SshServer sshServer) {
38 | if (sshServer.getOrder() == -1) {
39 | sshServer.setOrder(terminals.size());
40 | }
41 | sshServerDataSource
42 | .addSshServer(sshServer)
43 | .observeForever(b -> {
44 | if (!b) {
45 | return;
46 | }
47 | loadTerminals();
48 | });
49 | }
50 |
51 | public void deleteServer(final TerminalItem item) {
52 | if (item.getSshServer() == null) {
53 | return;
54 | }
55 | sshServerDataSource.delete(item.getSshServer()).observeForever(b -> {
56 | if (!b) {
57 | return;
58 | }
59 | terminals.remove(item);
60 | });
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/fragment/RetainedDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.common.fragment;
2 |
3 | import android.os.Bundle;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 | import androidx.fragment.app.DialogFragment;
8 | import androidx.fragment.app.FragmentManager;
9 | import androidx.fragment.app.FragmentTransaction;
10 |
11 | public class RetainedDialogFragment extends DialogFragment {
12 |
13 | @Override
14 | public void onCreate(@Nullable Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setRetainInstance(true);
17 | }
18 |
19 | @Override
20 | public void onDestroyView() {
21 | if (getDialog() != null && getRetainInstance()) {
22 | getDialog().setDismissMessage(null);
23 | }
24 | super.onDestroyView();
25 | }
26 |
27 | @Override
28 | public void show(@NonNull FragmentManager manager, String tag) {
29 | try {
30 | super.show(manager, tag);
31 | } catch (Exception e) {
32 | FragmentTransaction transaction = manager.beginTransaction();
33 | transaction.add(this, tag);
34 | transaction.commitAllowingStateLoss();
35 | }
36 | }
37 |
38 | public interface ResultCallback {
39 | void onResult(String text);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/fragment/TextFieldDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.common.fragment;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.app.Dialog;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.os.Bundle;
9 | import android.os.Handler;
10 | import android.os.Looper;
11 | import android.text.InputType;
12 | import android.text.TextUtils;
13 | import android.view.LayoutInflater;
14 | import android.view.View;
15 | import android.view.inputmethod.InputMethodManager;
16 | import android.widget.Button;
17 | import android.widget.EditText;
18 |
19 | import androidx.annotation.NonNull;
20 | import androidx.appcompat.app.AlertDialog;
21 | import androidx.fragment.app.FragmentActivity;
22 |
23 | import com.github.maoabc.BaseApp;
24 | import com.github.maoabc.aterm.R;
25 | import com.github.maoabc.util.Precondition;
26 | import com.google.android.material.textfield.TextInputLayout;
27 |
28 |
29 | /**
30 | * 通用文本输入,用来新建或者重命名文件等
31 | * Created by mao on 17-8-15.
32 | */
33 |
34 | public class TextFieldDialogFragment extends RetainedDialogFragment {
35 | private static final String TITLE_KEY = "title_key";
36 | private static final String HINT_KEY = "hint_key";
37 | private static final String TEXT_KEY = "text_key";
38 | private static final String SELECT_ALL_KEY = "select_all_key";
39 | private static final String INPUT_TYPE_KEY = "input_type_key";
40 |
41 |
42 | private String mTitle;
43 |
44 | private String mHint;
45 | private String mText;
46 | private int mInputType;
47 |
48 | private boolean mSelectAll;
49 |
50 | private EditText mInputText;
51 | private TextInputLayout inputLayout;
52 |
53 | private ResultCallback mResultCallback;
54 |
55 | private final Handler mHandler = new Handler(Looper.getMainLooper());
56 |
57 | public static TextFieldDialogFragment newInstance(String title, String hint, String initText) {
58 | return newInstance(title, hint, initText, false, InputType.TYPE_CLASS_TEXT);
59 | }
60 |
61 | public static TextFieldDialogFragment newInstance(String title, String hint, String initText, boolean selectAll) {
62 | return newInstance(title, hint, initText, selectAll, InputType.TYPE_CLASS_TEXT);
63 | }
64 |
65 | public static TextFieldDialogFragment newInstance(String title, String hint, String initText, boolean selectAll, int inputType) {
66 |
67 | Bundle args = new Bundle();
68 |
69 | TextFieldDialogFragment fragment = new TextFieldDialogFragment();
70 | args.putString(TITLE_KEY, title);
71 | args.putString(HINT_KEY, hint);
72 | args.putString(TEXT_KEY, initText);
73 | args.putBoolean(SELECT_ALL_KEY, selectAll);
74 | args.putInt(INPUT_TYPE_KEY, inputType);
75 |
76 | fragment.setArguments(args);
77 | return fragment;
78 | }
79 |
80 | @Override
81 | public void onAttach(@NonNull Context context) {
82 | super.onAttach(context);
83 |
84 | Bundle args = getArguments();
85 | Precondition.checkNotNull(args);
86 |
87 | mTitle = args.getString(TITLE_KEY, "");
88 | mHint = args.getString(HINT_KEY, "");
89 | mText = args.getString(TEXT_KEY, "");
90 | mSelectAll = args.getBoolean(SELECT_ALL_KEY);
91 | mInputType = args.getInt(INPUT_TYPE_KEY, InputType.TYPE_CLASS_TEXT);
92 |
93 | }
94 |
95 | @NonNull
96 | @Override
97 | public Dialog onCreateDialog(Bundle savedInstanceState) {
98 |
99 | LayoutInflater inflater = LayoutInflater.from(getContext());
100 | @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.fragment_text_field, null);
101 |
102 | inputLayout = view.findViewById(R.id.input_layout);
103 | mInputText = view.findViewById(R.id.et_input);
104 |
105 |
106 | FragmentActivity context = getActivity();
107 | Precondition.checkNotNull(context);
108 |
109 | AlertDialog alertDialog = new AlertDialog.Builder(context)
110 | .setTitle(mTitle)
111 | .setView(view)
112 | .setPositiveButton(R.string.ok, null)
113 | .setNegativeButton(R.string.cancel, null)
114 | .create();
115 |
116 | alertDialog.setOnShowListener(dialog -> {
117 | initEditText();
118 | AlertDialog ad = (AlertDialog) dialog;
119 | Button button = ad.getButton(DialogInterface.BUTTON_POSITIVE);
120 | button.setOnClickListener(v -> {
121 | CharSequence text = mInputText.getText().toString().trim();
122 | if (TextUtils.isEmpty(text)) {
123 | BaseApp.toast(R.string.empty_text);
124 | return;
125 | }
126 | if (!text.equals(mText) && mResultCallback != null) {
127 | mResultCallback.onResult(text.toString());
128 | }
129 | hideSoftInput();
130 | dismiss();
131 | });
132 | });
133 | return alertDialog;
134 | }
135 |
136 | private void initEditText() {
137 | inputLayout.setHint(mHint);
138 | mInputText.requestFocus();
139 | if (!TextUtils.isEmpty(mText)) {
140 | mInputText.setText(mText);
141 | if (mSelectAll) {
142 | mInputText.setSelection(0, mText.length());
143 | } else {
144 | int i = mText.lastIndexOf('.');
145 | mInputText.setSelection(0, i == -1 ? mText.length() : i);
146 | }
147 | }
148 | mInputText.setInputType(mInputType);
149 | mHandler.postDelayed(() -> {
150 | Context context = getContext();
151 | if (context != null) {
152 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
153 | if (imm != null) imm.showSoftInput(mInputText, 0);
154 | }
155 | }, 100);
156 | }
157 |
158 | private void hideSoftInput() {
159 | Activity context = getActivity();
160 | if (context != null) {
161 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
162 | if (imm != null) imm.hideSoftInputFromWindow(mInputText.getWindowToken(), 0);
163 | }
164 | }
165 |
166 |
167 | public void setResultCallback(ResultCallback resultCallback) {
168 | this.mResultCallback = resultCallback;
169 | }
170 |
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/widget/CheckableButton.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.maoabc.common.widget;
18 |
19 | import android.content.Context;
20 | import android.util.AttributeSet;
21 | import android.widget.Checkable;
22 | import android.widget.LinearLayout;
23 |
24 | import androidx.appcompat.widget.AppCompatButton;
25 |
26 | /**
27 | * This is a simple wrapper for {@link LinearLayout} that implements the {@link Checkable}
28 | * interface by keeping an internal 'checked' state flag.
29 | *
30 | * This can be used as the root view for a custom list item layout for
31 | * {@link android.widget.AbsListView} elements with a
32 | * {@link android.widget.AbsListView#setChoiceMode(int) choiceMode} set.
33 | */
34 | public class CheckableButton extends AppCompatButton implements Checkable {
35 | private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
36 |
37 | private boolean mChecked = false;
38 |
39 | public CheckableButton(Context context) {
40 | super(context);
41 | }
42 |
43 | public CheckableButton(Context context, AttributeSet attrs) {
44 | super(context, attrs);
45 | }
46 |
47 | public CheckableButton(Context context, AttributeSet attrs, int defStyleAttr) {
48 | super(context, attrs, defStyleAttr);
49 | }
50 |
51 |
52 | public boolean isChecked() {
53 | return mChecked;
54 | }
55 |
56 | public void setChecked(boolean b) {
57 | if (b != mChecked) {
58 | mChecked = b;
59 | refreshDrawableState();
60 | }
61 | }
62 |
63 | public void toggle() {
64 | setChecked(!mChecked);
65 | }
66 |
67 | @Override
68 | public int[] onCreateDrawableState(int extraSpace) {
69 | final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
70 | if (drawableState != null && isChecked()) {
71 | mergeDrawableStates(drawableState, CHECKED_STATE_SET);
72 | }
73 | return drawableState;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/widget/CheckableDividerRelativeLayout.java:
--------------------------------------------------------------------------------
1 |
2 | package com.github.maoabc.common.widget;
3 |
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.widget.Checkable;
7 |
8 | public class CheckableDividerRelativeLayout extends DividerRelativeLayout implements Checkable {
9 | private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};
10 |
11 | private boolean mChecked = false;
12 |
13 | public CheckableDividerRelativeLayout(Context context) {
14 | super(context);
15 | }
16 |
17 | public CheckableDividerRelativeLayout(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public CheckableDividerRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
22 | super(context, attrs, defStyleAttr);
23 | }
24 |
25 | public boolean isChecked() {
26 | return mChecked;
27 | }
28 |
29 | public void setChecked(boolean b) {
30 | if (b != mChecked) {
31 | mChecked = b;
32 | refreshDrawableState();
33 | }
34 | }
35 |
36 |
37 | public void toggle() {
38 | setChecked(!mChecked);
39 | }
40 |
41 | @Override
42 | public int[] onCreateDrawableState(int extraSpace) {
43 | final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
44 | if (drawableState != null && isChecked()) {
45 | mergeDrawableStates(drawableState, CHECKED_STATE_SET);
46 | }
47 | return drawableState;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/widget/DividerRelativeLayout.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.common.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.util.AttributeSet;
9 | import android.widget.RelativeLayout;
10 |
11 | import com.github.maoabc.aterm.R;
12 |
13 |
14 | public class DividerRelativeLayout extends RelativeLayout {
15 | private Paint paint;
16 | private float startX;
17 |
18 | public DividerRelativeLayout(Context context) {
19 | super(context);
20 | init(context, null);
21 | }
22 |
23 | public DividerRelativeLayout(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | init(context, attrs);
26 | }
27 |
28 | public DividerRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
29 | super(context, attrs, defStyleAttr);
30 | init(context, attrs);
31 | }
32 |
33 | private void init(Context context, AttributeSet attrs) {
34 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DividerRelativeLayout);
35 | boolean b = a.getBoolean(R.styleable.DividerRelativeLayout_dividerEnable, false);
36 | if (b) {
37 | paint = new Paint();
38 | int color = a.getColor(R.styleable.DividerRelativeLayout_dividerColor, Color.LTGRAY);
39 | paint.setColor(color);
40 | startX = a.getDimension(R.styleable.DividerRelativeLayout_dividerStart, 0);
41 | }
42 |
43 | a.recycle();
44 | }
45 |
46 | @Override
47 | protected void onDraw(Canvas canvas) {
48 | super.onDraw(canvas);
49 | if (paint != null) {
50 | canvas.drawLine(startX, getHeight() - 1, getWidth(), getHeight() - 1, paint);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/widget/FixedLinearLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.common.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.Log;
6 |
7 | import androidx.recyclerview.widget.LinearLayoutManager;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 |
11 | public class FixedLinearLayoutManager extends LinearLayoutManager {
12 | public FixedLinearLayoutManager(Context context) {
13 | super(context);
14 | }
15 |
16 | public FixedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
17 | super(context, orientation, reverseLayout);
18 | }
19 |
20 | public FixedLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
21 | super(context, attrs, defStyleAttr, defStyleRes);
22 | }
23 |
24 | @Override
25 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
26 | try {
27 | super.onLayoutChildren(recycler, state);
28 | } catch (IndexOutOfBoundsException e) {
29 | Log.e("LinearLayoutManager", "onLayoutChildren: ", e);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/common/widget/LongPressRepeatImageView.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.common.widget;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 | import android.view.ViewConfiguration;
8 |
9 | import androidx.appcompat.widget.AppCompatImageView;
10 |
11 | public class LongPressRepeatImageView extends AppCompatImageView {
12 |
13 | private int[] PRESSED_STATE_SET = {android.R.attr.state_pressed};
14 | private float mLastX;
15 | private float mLastY;
16 |
17 | public LongPressRepeatImageView(Context context) {
18 | super(context);
19 | }
20 |
21 | public LongPressRepeatImageView(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public LongPressRepeatImageView(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | private int mPressedCount;
30 | private boolean mTouchDown = false;
31 |
32 | // private final Handler mHandler = new Handler();
33 |
34 | private final Runnable mClickRunnable = new Runnable() {
35 | @Override
36 | public void run() {
37 | mPressedCount++;
38 | performClick();
39 | // Log.d("LongPress", "run: " + mPressedCount);
40 | postDelayed(this, ViewConfiguration.getKeyRepeatDelay());
41 | }
42 | };
43 |
44 | @SuppressLint("ClickableViewAccessibility")
45 | @Override
46 | public boolean onTouchEvent(MotionEvent event) {
47 | switch (event.getAction()) {
48 | case MotionEvent.ACTION_DOWN: {
49 | mTouchDown = true;
50 | refreshDrawableState();
51 |
52 | mPressedCount = 0;
53 | mLastX = event.getX();
54 | mLastY = event.getY();
55 |
56 |
57 | // mHandler.postDelayed(mClickRunnable, 400);
58 | postDelayed(mClickRunnable, ViewConfiguration.getLongPressTimeout());
59 |
60 |
61 | break;
62 | }
63 | case MotionEvent.ACTION_UP:
64 | case MotionEvent.ACTION_CANCEL: {
65 | removeCallbacks(mClickRunnable);
66 | mTouchDown = false;
67 | refreshDrawableState();
68 | if (mPressedCount == 0 && (Math.abs(event.getX() - mLastX) < 20
69 | && Math.abs(event.getY() - mLastY) < 20)) {//没有长按,且手指在很小的范围移动,则当成单击事件
70 | performClick();
71 | }
72 |
73 | // mHandler.removeCallbacks(mClickRunnable);
74 | }
75 | }
76 | return true;
77 | }
78 |
79 | @Override
80 | public int[] onCreateDrawableState(int extraSpace) {
81 | final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
82 | if (drawableState != null && mTouchDown) {
83 | mergeDrawableStates(drawableState, PRESSED_STATE_SET);
84 | }
85 | return drawableState;
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/util/AppExecutors.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.util;
2 |
3 | /**
4 | * Created by mao on 17-11-19.
5 | */
6 |
7 | import android.os.Handler;
8 | import android.os.Looper;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | import java.util.concurrent.Executor;
13 | import java.util.concurrent.Executors;
14 |
15 | /**
16 | * Global executor pools for the whole application.
17 | *
18 | * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
19 | * webservice requests).
20 | */
21 | public class AppExecutors {
22 |
23 | private final Executor mDiskIO;
24 |
25 | private final Executor mNetworkIO;
26 |
27 | private final Executor mMainThread;
28 |
29 | private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
30 | this.mDiskIO = diskIO;
31 | this.mNetworkIO = networkIO;
32 | this.mMainThread = mainThread;
33 | }
34 |
35 | public AppExecutors() {
36 | this(Executors.newScheduledThreadPool(2), Executors.newFixedThreadPool(2),
37 | new MainThreadExecutor());
38 | }
39 |
40 | public Executor diskIO() {
41 | return mDiskIO;
42 | }
43 |
44 | public Executor networkIO() {
45 | return mNetworkIO;
46 | }
47 |
48 | public Executor mainThread() {
49 | return mMainThread;
50 | }
51 |
52 | private static class MainThreadExecutor implements Executor {
53 | private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
54 |
55 | @Override
56 | public void execute(@NonNull Runnable command) {
57 | mainThreadHandler.post(command);
58 | }
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/util/FileUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.util;
2 |
3 | import android.content.ContentResolver;
4 | import android.database.Cursor;
5 | import android.net.Uri;
6 | import android.provider.MediaStore;
7 |
8 | import com.github.maoabc.BaseApp;
9 |
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.io.InputStream;
13 | import java.io.OutputStream;
14 |
15 | public class FileUtils {
16 |
17 | public static String getFileFromUri(Uri uri) {
18 |
19 | String path;
20 | if (uri == null || (path = uri.getPath()) == null) {
21 | return null;
22 | }
23 |
24 | String scheme = uri.getScheme();
25 | if (ContentResolver.SCHEME_FILE.equals(scheme)) {
26 | return path;
27 | }
28 | Cursor cursor = null;
29 | try {
30 | if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
31 | ContentResolver contentResolver = BaseApp.get().getContentResolver();
32 | String authority = uri.getAuthority();
33 | if (MediaStore.AUTHORITY.equals(authority) || "downloads".equals(authority))
34 | cursor = contentResolver.query(uri, new String[]{MediaStore.MediaColumns.DATA}, null, null, null);
35 | if (cursor != null && cursor.moveToFirst() && !cursor.isNull(0)) {
36 | return cursor.getString(0);
37 | }
38 | }
39 | } finally {
40 | if (cursor != null) cursor.close();
41 | }
42 | if (path.startsWith("/root")) {//adm
43 | path = path.substring(5);
44 | }
45 |
46 | if (new File(path).exists()) {
47 | return path;
48 | }
49 | return "";
50 | }
51 |
52 | public static void copyStream(InputStream input, OutputStream output) throws IOException {
53 | byte[] buf = new byte[64 * 1024];
54 | int len;
55 | while ((len = input.read(buf, 0, buf.length)) != -1) {
56 | output.write(buf, 0, len);
57 | }
58 | }
59 |
60 | public static void copyStreamAndClose(InputStream input, OutputStream output) {
61 | try {
62 | copyStream(input, output);
63 | } catch (IOException e) {
64 |
65 | } finally {
66 | try {
67 | input.close();
68 | } catch (IOException ignored) {
69 | }
70 | try {
71 | output.close();
72 | } catch (IOException ignored) {
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/util/MimeTypes.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.util;
2 |
3 | import android.webkit.MimeTypeMap;
4 |
5 | import androidx.annotation.IntDef;
6 | import androidx.annotation.NonNull;
7 |
8 | import java.util.HashMap;
9 | import java.util.regex.Pattern;
10 |
11 | public final class MimeTypes {
12 |
13 | public static final String SCHEME_ARCHIVE = "archive";
14 |
15 | public static final Pattern ARCHIVE_PATTERN = Pattern.compile("^.*\\.(?i)(zip|Z|rpm|deb|cpio|lzma|arj|xar|lzh|zst|cab|z[0-9]+|rar|xz|tar|tgz|tbz2|txz|jar|gz|bz2|7z|7z.001)$");
16 | public static final Pattern MUSIC_PATTERN = Pattern.compile("^.*\\.(?i)(mp3|wma|wav|aac|ogg|m4a|flac|ra|rm|amr|mid)$");
17 | // public static final Pattern docP = Pattern.compile("^.*\\.(?i)(doc|docx|rtf|odt)$");
18 | public static final Pattern MOVIE_PATTERN = Pattern.compile("^.*\\.(?i)(3gp|3gpp|mp4|avi|flv|mkv|m4v|rmvb|mpg|mpeg|wmv|mov|vob|ts|divx|asf|avchd|mts|m2ts|webm|wtv)$");
19 | public static final Pattern IMAGE_PATTERN = Pattern.compile("^.*\\.(?i)(jpg|jpeg|bmp|png|gif|tiff|arw|dng|raw|rw2|srw|webp)$");
20 | // public static final Pattern MARKUP_PATTERN = Pattern.compile("^.*\\.(?i)(html|xhtml|xml|css|tex|json)$");
21 | public static final Pattern SCRIPT_PATTERN = Pattern.compile("^.*\\.(?i)(sh|py|bat|awk|sed|bash|zsh|csh)$");
22 | // public static final Pattern excelP = Pattern.compile("^.*\\.(?i)(xls|xlsx|ods)$");
23 | // public static final Pattern pwrpointP = Pattern.compile("^.*\\.(?i)(ppt|pptx|pps|ppsx|odp)$");
24 | public static final Pattern TEXT_PATTERN = Pattern.compile("^.*\\.(?i)(txt|log|cfg|ini|rc|prop|csv|conf|java|h|hpp|c|cc|cpp|cxx|js|py|lua|diff|md|html|json|lisp|php|rb|rs|pl|go|awk|sed|hs)$");
25 | public static final Pattern APK_PATTERN = Pattern.compile("^.*\\.(?i)(apk)$");
26 |
27 | public static final Pattern SHELL_SCRIPT_PATTERN = Pattern.compile("^.*\\.(?i)(sh|bash|zsh)$");
28 |
29 | @IntDef({OTHER, APK, IMAGE, TEXT, MUSIC, MOVIE, ARCHIVE, SCRIPT})
30 | @interface MimeType {
31 | }
32 |
33 | public static final int OTHER = 0;
34 | public static final int APK = 1;
35 | public static final int IMAGE = 2;
36 | public static final int TEXT = 3;
37 | public static final int MUSIC = 4;
38 | public static final int MOVIE = 5;
39 | public static final int ARCHIVE = 6;
40 | public static final int SCRIPT = 7;
41 |
42 | public static final String ALL_MIME_TYPES = "*/*";
43 | private static final String ALL_TEXT_TYPES = "text/*";
44 |
45 | public static final String MIME_APK = "application/vnd.android.package-archive";
46 | public static final String MIME_ZIP = "application/zip";
47 | public static final String MIME_RAR = "application/rar";
48 |
49 | public static final String MIME_7ZIP = "application/x-7z-compressed";
50 |
51 | public static final String MIME_BZIP2 = "application/x-bzip2";
52 |
53 | public static final String MIME_GZIP = "application/x-gzip";
54 |
55 | public static final String MIME_TAR = "application/x-tar";
56 |
57 | public static final String MIME_XZ = "application/x-xz";
58 |
59 | public static final String MIME_ZSTD = "application/zstd";
60 |
61 | public static final String MIME_LZMA = "application/x-lzma";
62 |
63 | public static final String MIME_CPIO = "application/x-cpio";
64 |
65 | public static final String MIME_Z = "application/x-compress";
66 |
67 | // construct a with an approximation of the capacity
68 | private static final HashMap MIME_TYPES = new HashMap<>(1 + (int) (66 / 0.75));
69 |
70 | static {
71 |
72 | /*
73 | * ================= MIME TYPES ====================
74 | */
75 | MIME_TYPES.put("asm", "text/x-asm");
76 | MIME_TYPES.put("json", "application/json");
77 | MIME_TYPES.put("js", "application/javascript");
78 |
79 | MIME_TYPES.put("def", "text/plain");
80 | MIME_TYPES.put("in", "text/plain");
81 | MIME_TYPES.put("list", "text/plain");
82 | MIME_TYPES.put("log", "text/plain");
83 | MIME_TYPES.put("pl", "text/plain");
84 | MIME_TYPES.put("prop", "text/plain");
85 | MIME_TYPES.put("properties", "text/plain");
86 | MIME_TYPES.put("rc", "text/plain");
87 | MIME_TYPES.put("ini", "text/plain");
88 | MIME_TYPES.put("md", "text/markdown");
89 |
90 | MIME_TYPES.put("epub", "application/epub+zip");
91 | MIME_TYPES.put("ibooks", "application/x-ibooks+zip");
92 |
93 | MIME_TYPES.put("ifb", "text/calendar");
94 | MIME_TYPES.put("eml", "message/rfc822");
95 | MIME_TYPES.put("msg", "application/vnd.ms-outlook");
96 |
97 | MIME_TYPES.put("ace", "application/x-ace-compressed");
98 | MIME_TYPES.put("7z", "application/x-7z-compressed");
99 | MIME_TYPES.put("bz", "application/x-bzip");
100 | MIME_TYPES.put("bz2", "application/x-bzip2");
101 | MIME_TYPES.put("cab", "application/vnd.ms-cab-compressed");
102 | MIME_TYPES.put("gz", "application/x-gzip");
103 | MIME_TYPES.put("lrf", "application/octet-stream");
104 | MIME_TYPES.put("jar", "application/java-archive");
105 | MIME_TYPES.put("xz", "application/x-xz");
106 | MIME_TYPES.put("tar", "application/x-tar");
107 | MIME_TYPES.put("Z", "application/x-compress");
108 | MIME_TYPES.put("lzma", "application/x-lzma");
109 |
110 | MIME_TYPES.put("bat", "application/x-bat");
111 | MIME_TYPES.put("ksh", "text/plain");
112 | MIME_TYPES.put("sh", "application/x-sh");
113 | MIME_TYPES.put("csh", "application/x-csh");
114 | MIME_TYPES.put("php", "text/x-php");
115 | MIME_TYPES.put("lisp", "text/x-script.lisp");
116 |
117 | MIME_TYPES.put("db", "application/octet-stream");
118 | MIME_TYPES.put("db3", "application/octet-stream");
119 |
120 | MIME_TYPES.put("otf", "application/x-font-otf");
121 | MIME_TYPES.put("ttf", "application/x-font-ttf");
122 | MIME_TYPES.put("psf", "application/x-font-linux-psf");
123 |
124 | MIME_TYPES.put("cgm", "image/cgm");
125 | MIME_TYPES.put("btif", "image/prs.btif");
126 | MIME_TYPES.put("dwg", "image/vnd.dwg");
127 | MIME_TYPES.put("dxf", "image/vnd.dxf");
128 | MIME_TYPES.put("fbs", "image/vnd.fastbidsheet");
129 | MIME_TYPES.put("fpx", "image/vnd.fpx");
130 | MIME_TYPES.put("fst", "image/vnd.fst");
131 | MIME_TYPES.put("mdi", "image/vnd.ms-mdi");
132 | MIME_TYPES.put("npx", "image/vnd.net-fpx");
133 | MIME_TYPES.put("xif", "image/vnd.xiff");
134 | MIME_TYPES.put("pct", "image/x-pict");
135 | MIME_TYPES.put("pic", "image/x-pict");
136 |
137 | MIME_TYPES.put("adp", "audio/adpcm");
138 | MIME_TYPES.put("au", "audio/basic");
139 | MIME_TYPES.put("snd", "audio/basic");
140 | MIME_TYPES.put("m2a", "audio/mpeg");
141 | MIME_TYPES.put("m3a", "audio/mpeg");
142 | MIME_TYPES.put("oga", "audio/ogg");
143 | MIME_TYPES.put("spx", "audio/ogg");
144 | MIME_TYPES.put("aac", "audio/x-aac");
145 | MIME_TYPES.put("mka", "audio/x-matroska");
146 |
147 | MIME_TYPES.put("jpgv", "video/jpeg");
148 | MIME_TYPES.put("jpgm", "video/jpm");
149 | MIME_TYPES.put("jpm", "video/jpm");
150 | MIME_TYPES.put("mj2", "video/mj2");
151 | MIME_TYPES.put("mjp2", "video/mj2");
152 | MIME_TYPES.put("mpa", "video/mpeg");
153 | MIME_TYPES.put("ogv", "video/ogg");
154 | MIME_TYPES.put("flv", "video/x-flv");
155 | MIME_TYPES.put("mkv", "video/x-matroska");
156 |
157 | }
158 |
159 |
160 | /**
161 | * Get Mime Type of a file
162 | *
163 | * @param name the file of which mime type to get
164 | * @return Mime type in form of String
165 | */
166 | @NonNull
167 | public static String getMimeType(String name) {
168 |
169 | final String extension = getExtension(name);
170 |
171 | String type = getMimeTypeFromExtension(extension);
172 | // if (type == null) {
173 | // return "application/octet-stream";
174 | // }
175 | return type == null ? "application/octet-stream" : type;
176 | }
177 |
178 | public static String getMimeTypeFromExtension(String extension) {
179 | String mimeType = null;
180 | // mapping extension to system mime types
181 | if (!extension.isEmpty()) {
182 | final String extensionLowerCase = extension.toLowerCase();
183 | mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extensionLowerCase);
184 | if (mimeType == null) {
185 | mimeType = MIME_TYPES.get(extensionLowerCase);
186 | }
187 | }
188 | return mimeType;
189 | }
190 |
191 | @MimeType
192 | public static int getSupportMimeType(String name) {
193 | if (isAPK(name)) {
194 | return APK;
195 | } else if (isImage(name)) {
196 | return IMAGE;
197 | } else if (isMusic(name)) {
198 | return MUSIC;
199 | } else if (isMovie(name)) {
200 | return MOVIE;
201 | } else if (isText(name)) {
202 | return TEXT;
203 | } else if (isArchive(name)) {
204 | return ARCHIVE;
205 | } else if (isScript(name)) {
206 | return SCRIPT;
207 | } else {
208 | return OTHER;
209 | }
210 | }
211 |
212 | public static boolean isAPK(String name) {
213 | return APK_PATTERN.matcher(name).matches();
214 | }
215 |
216 | public static boolean isImage(String name) {
217 | return IMAGE_PATTERN.matcher(name).matches();
218 | }
219 |
220 | public static boolean isText(String name) {
221 | return TEXT_PATTERN.matcher(name).matches();
222 | }
223 |
224 | public static boolean isMusic(String name) {
225 | return MUSIC_PATTERN.matcher(name).matches();
226 | }
227 |
228 | public static boolean isMovie(String name) {
229 | return MOVIE_PATTERN.matcher(name).matches();
230 | }
231 |
232 | public static boolean isArchive(String name) {
233 | return ARCHIVE_PATTERN.matcher(name).matches();
234 | }
235 |
236 | public static boolean isScript(String name) {
237 | return SCRIPT_PATTERN.matcher(name).matches();
238 | }
239 |
240 | public static boolean isShellScript(String name) {
241 | return SHELL_SCRIPT_PATTERN.matcher(name).matches();
242 | }
243 |
244 |
245 | /**
246 | * Helper method for {@link #getMimeType(String)}
247 | * to calculate the last '.' extension of files
248 | *
249 | * @param name the path of file
250 | * @return extension extracted from name in lowercase
251 | */
252 | @NonNull
253 | public static String getExtension(@NonNull String name) {
254 | int index = name.lastIndexOf('.');
255 | /*.开头的文件不算后缀*/
256 | return index > 0 ? name.substring(index + 1).toLowerCase() : "";
257 | }
258 |
259 | }
260 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/util/Precondition.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.util;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | /**
6 | * Created by mao on 18-2-24.
7 | */
8 |
9 | public class Precondition {
10 |
11 | private Precondition() {
12 | }
13 |
14 | public static void checkArgument(boolean expression) {
15 | if (!expression) {
16 | throw new IllegalArgumentException();
17 | }
18 | }
19 |
20 | public static void checkArgument(boolean expression, @Nullable Object errorMessage) {
21 | if (!expression) {
22 | throw new IllegalArgumentException(String.valueOf(errorMessage));
23 | }
24 | }
25 |
26 | public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
27 | if (!expression) {
28 | throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
29 | }
30 | }
31 |
32 | public static void checkState(boolean expression) {
33 | if (!expression) {
34 | throw new IllegalStateException();
35 | }
36 | }
37 |
38 | public static void checkState(boolean expression, @Nullable Object errorMessage) {
39 | if (!expression) {
40 | throw new IllegalStateException(String.valueOf(errorMessage));
41 | }
42 | }
43 |
44 | public static void checkState(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
45 | if (!expression) {
46 | throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs));
47 | }
48 | }
49 |
50 | public static T checkNotNull(T reference) {
51 | if (reference == null) {
52 | throw new NullPointerException();
53 | } else {
54 | return reference;
55 | }
56 | }
57 |
58 | public static T checkNotNull(T reference, @Nullable Object errorMessage) {
59 | if (reference == null) {
60 | throw new NullPointerException(String.valueOf(errorMessage));
61 | } else {
62 | return reference;
63 | }
64 | }
65 |
66 | public static T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
67 | if (reference == null) {
68 | throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
69 | } else {
70 | return reference;
71 | }
72 | }
73 |
74 | public static int checkElementIndex(int index, int size) {
75 | return checkElementIndex(index, size, "index");
76 | }
77 |
78 | public static int checkElementIndex(int index, int size, @Nullable String desc) {
79 | if (index >= 0 && index < size) {
80 | return index;
81 | } else {
82 | throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
83 | }
84 | }
85 |
86 | private static String badElementIndex(int index, int size, String desc) {
87 | if (index < 0) {
88 | return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)});
89 | } else if (size < 0) {
90 | throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString());
91 | } else {
92 | return format("%s (%s) must be less than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)});
93 | }
94 | }
95 |
96 | public static int checkPositionIndex(int index, int size) {
97 | return checkPositionIndex(index, size, "index");
98 | }
99 |
100 | public static int checkPositionIndex(int index, int size, @Nullable String desc) {
101 | if (index >= 0 && index <= size) {
102 | return index;
103 | } else {
104 | throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
105 | }
106 | }
107 |
108 | private static String badPositionIndex(int index, int size, String desc) {
109 | if (index < 0) {
110 | return format("%s (%s) must not be negative", new Object[]{desc, Integer.valueOf(index)});
111 | } else if (size < 0) {
112 | throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString());
113 | } else {
114 | return format("%s (%s) must not be greater than size (%s)", new Object[]{desc, Integer.valueOf(index), Integer.valueOf(size)});
115 | }
116 | }
117 |
118 | public static void checkPositionIndexes(int start, int end, int size) {
119 | if (start < 0 || end < start || end > size) {
120 | throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
121 | }
122 | }
123 |
124 | private static String badPositionIndexes(int start, int end, int size) {
125 | return start >= 0 && start <= size ? (end >= 0 && end <= size ? format("end index (%s) must not be less than execute index (%s)", new Object[]{Integer.valueOf(end), Integer.valueOf(start)}) : badPositionIndex(end, size, "end index")) : badPositionIndex(start, size, "execute index");
126 | }
127 |
128 | private static String format(String template, @Nullable Object... args) {
129 | template = String.valueOf(template);
130 | StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
131 | int templateStart = 0;
132 |
133 | int i;
134 | int placeholderStart;
135 | for (i = 0; i < args.length; templateStart = placeholderStart + 2) {
136 | placeholderStart = template.indexOf("%s", templateStart);
137 | if (placeholderStart == -1) {
138 | break;
139 | }
140 |
141 | builder.append(template.substring(templateStart, placeholderStart));
142 | builder.append(args[i++]);
143 | }
144 |
145 | builder.append(template.substring(templateStart));
146 | if (i < args.length) {
147 | builder.append(" [");
148 | builder.append(args[i++]);
149 |
150 | while (i < args.length) {
151 | builder.append(", ");
152 | builder.append(args[i++]);
153 | }
154 |
155 | builder.append(']');
156 | }
157 |
158 | return builder.toString();
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/maoabc/util/RecyclerViewAdapterChangedCallback.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc.util;
2 |
3 | import androidx.databinding.ObservableList;
4 | import androidx.recyclerview.widget.RecyclerView;
5 |
6 | import java.lang.ref.WeakReference;
7 |
8 | public class RecyclerViewAdapterChangedCallback extends ObservableList.OnListChangedCallback {
9 | private final WeakReference adapterWeakReference;
10 |
11 | public RecyclerViewAdapterChangedCallback(RecyclerView.Adapter adapter) {
12 | this.adapterWeakReference = new WeakReference<>(adapter);
13 | }
14 |
15 | @Override
16 | public void onChanged(ObservableList sender) {
17 | RecyclerView.Adapter adapter = adapterWeakReference.get();
18 | if (adapter != null) {
19 | adapter.notifyDataSetChanged();
20 | }
21 |
22 | }
23 |
24 | @Override
25 | public void onItemRangeChanged(ObservableList sender, int positionStart, int itemCount) {
26 | RecyclerView.Adapter adapter = adapterWeakReference.get();
27 | if (adapter != null) {
28 | adapter.notifyItemRangeChanged(positionStart, itemCount);
29 | }
30 |
31 | }
32 |
33 | @Override
34 | public void onItemRangeInserted(ObservableList sender, int positionStart, int itemCount) {
35 | RecyclerView.Adapter adapter = adapterWeakReference.get();
36 | if (adapter != null) {
37 | adapter.notifyItemRangeInserted(positionStart, itemCount);
38 | }
39 |
40 | }
41 |
42 | @Override
43 | public void onItemRangeMoved(ObservableList sender, int fromPosition, int toPosition, int itemCount) {
44 | RecyclerView.Adapter adapter = adapterWeakReference.get();
45 | if (adapter != null) {
46 | for (int i = 0; i < itemCount; i++) {
47 | adapter.notifyItemMoved(fromPosition + i, toPosition + i);
48 | }
49 | }
50 |
51 | }
52 |
53 | @Override
54 | public void onItemRangeRemoved(ObservableList sender, int positionStart, int itemCount) {
55 | RecyclerView.Adapter adapter = adapterWeakReference.get();
56 | if (adapter != null) {
57 | adapter.notifyItemRangeRemoved(positionStart, itemCount);
58 | }
59 |
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/res/color/checked_button_color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_stat_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/drawable-hdpi/ic_stat_terminal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_stat_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/drawable-mdpi/ic_stat_terminal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_stat_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/drawable-xhdpi/ic_stat_terminal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_stat_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/drawable-xxhdpi/ic_stat_terminal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_stat_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/drawable-xxxhdpi/ic_stat_terminal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_downward_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_forward_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_upward_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cancel_24dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_aterm.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
16 |
17 |
18 |
28 |
29 |
37 |
38 |
45 |
46 |
52 |
53 |
57 |
58 |
59 |
67 |
68 |
75 |
76 |
83 |
84 |
91 |
92 |
99 |
100 |
107 |
108 |
115 |
116 |
117 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
136 |
137 |
140 |
141 |
147 |
148 |
156 |
157 |
173 |
174 |
175 |
183 |
184 |
192 |
193 |
194 |
195 |
196 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_aterm_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_create_ssh_server.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
17 |
18 |
21 |
22 |
29 |
30 |
36 |
37 |
38 |
46 |
47 |
54 |
55 |
56 |
57 |
65 |
66 |
72 |
73 |
74 |
83 |
84 |
94 |
95 |
101 |
102 |
103 |
104 |
118 |
119 |
120 |
130 |
131 |
137 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_text_field.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/list_item.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
16 |
17 |
18 |
19 |
29 |
30 |
39 |
40 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/terminal_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
13 |
14 |
17 |
18 |
19 |
28 |
29 |
44 |
45 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | - 8
8 | - 9
9 | - 10
10 | - 12
11 | - 14
12 | - 16
13 | - 20
14 | - 24
15 | - 28
16 | - 32
17 | - 36
18 | - 42
19 | - 48
20 |
21 |
22 |
23 | - Black text on white
24 | - White text on black
25 | - White text on blue
26 | - Green text on black
27 | - Amber text on black
28 | - Red text on black
29 | - Holo blue text on black
30 | - Solarized Light
31 | - Solarized Dark
32 | - Linux Console
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arraysNoLocalize.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | - 8
7 | - 9
8 | - 10
9 | - 12
10 | - 14
11 | - 16
12 | - 20
13 | - 24
14 | - 28
15 | - 32
16 | - 36
17 | - 42
18 | - 48
19 |
20 |
21 |
22 | - 0
23 | - 1
24 | - 2
25 | - 3
26 | - 4
27 | - 5
28 | - 6
29 | - 7
30 | - 8
31 | - 9
32 |
33 |
34 |
35 |
36 | - 0
37 | - 1
38 | - 2
39 | - 3
40 | - 4
41 | - 5
42 | - 6
43 | - 7
44 |
45 |
46 |
47 | - 0
48 | - 1
49 | - 2
50 | - 3
51 | - 4
52 | - 5
53 | - 6
54 | - 7
55 |
56 |
57 |
58 | - vt100
59 | - screen
60 | - linux
61 | - screen-256color
62 | - xterm
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 | #000000
7 | #8A000000
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 36dp
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ATerm
3 | Run Script
4 |
5 | View
6 | Color
7 | Background Alpha
8 |
9 | Font Size
10 | Font size
11 | New Terminal
12 | Close
13 | New SSH
14 | Edit SSH
15 | Edit session name
16 | OK
17 | Cancel
18 | Text is empty
19 | Close terminal session
20 | Terminal session running
21 | [Process complete(%d) - press Enter]
22 | Host
23 | Port
24 | Username
25 | Password
26 | Use key
27 | Private key content
28 | Key path
29 | Key passphrase
30 | Host is empty
31 | Username is empty
32 | Need password or key
33 | Key format invalid
34 | Terminal settings
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
22 |
23 |
32 |
33 |
44 |
45 |
56 |
57 |
58 |
70 |
71 | 48dp
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/aterm_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
9 |
10 |
17 |
18 |
25 |
26 |
32 |
33 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_descriptor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/test/java/com/github/maoabc/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.maoabc;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.1.1'
12 |
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maoabc/aterminal/a527c7ee80eec0d2f1a25fb6e87167e88fa68838/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jun 27 20:21:54 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='aterm'
2 | include ':app'
3 | include ':terminalview'
4 | include ':pty'
5 |
--------------------------------------------------------------------------------