简单实现一个android终端软件

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:padding="3dp"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/input"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <EditText
            android:editable="false"
            android:clickable="false"
            android:focusable="false"
            android:id="@+id/hostname"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:gravity="center_vertical"
            android:textColor="@android:color/white"
            android:textSize="14sp"
            android:textStyle="bold" />
        
        <EditText
            android:id="@+id/script"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_toRightOf="@+id/hostname"
            android:layout_weight="1"
            android:gravity="left|center_vertical"
            android:imeOptions="actionGo"
            android:inputType="text"
            android:singleLine="true"
            android:textColor="@android:color/white" />
    </LinearLayout>

    <ScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/input">
        <TextView
            android:id="@+id/result"
            android:ellipsize="start"
            android:scrollbars="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white" />
    </ScrollView>
</RelativeLayout>

清单文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.Xten">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>
</manifest>

代码

package com.tencent.mmlite;

import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MainActivity extends Activity implements TextView.OnEditorActionListener {

    EditText cmd, hostname;
    TextView messageView;
    Handler handler;
    ScrollView scrollView;
    private static final String TAG = "Xten";
    static final String HOSTNAME = Build.DEVICE;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        cmd = findViewById(R.id.script);
        cmd.clearFocus();

        hostname = findViewById(R.id.hostname);
        hostname.setText(HOSTNAME + " $ ");

        messageView = findViewById(R.id.result);
        messageView.setMovementMethod(ScrollingMovementMethod.getInstance());

        scrollView = findViewById(R.id.scroll);
        scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                scrollView.post(new Runnable() {
                    public void run() {
                        scrollView.fullScroll(View.FOCUS_DOWN);
                    }
                });
            }
        });

        cmd.setOnEditorActionListener(this);

        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == 0x1) {
                    messageView.append("\n");
                    messageView.append(String.valueOf(msg.obj));
                }
            }
        };

        requestPermission(false);
    }

    private void println(String newText) {
        Message message = new Message();
        message.what = 0x1;
        message.obj = newText;
        handler.sendMessage(message);
    }

    public void onClick() {
        final String script = cmd.getText().toString().trim();
        cmd.setText("");

        if (TextUtils.isEmpty(script)) {
            println(HOSTNAME + " $ ");
            return;
        }

        if (TextUtils.equals("ctrl+c", script)) {
            reset();
            return;
        }

        if (TextUtils.equals("clear", script)) {
            messageView.setText("");
            return;
        }

        cmd.clearFocus();
        runScript(script);
    }

    private Thread scriptShell;
    private Thread normal, error;

    Process mProcess;
    InputStream err, is;

    private void reset() {
        if (mProcess != null) {
            mProcess.destroy();
            mProcess = null;
        }

        if (scriptShell != null && scriptShell.isAlive()) {
            scriptShell.interrupt();
            scriptShell = null;
        }

        if (normal != null && normal.isAlive()) {
            normal.interrupt();
            normal = null;
        }

        if (error != null && error.isAlive()) {
            error.interrupt();
            error = null;
        }

        if (err != null) {
            try {
                err.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void runScript(String text) {
        println("\n" + HOSTNAME + " $ " + text);
        reset();

        scriptShell = new Thread() {
            @Override
            public void run() {
                super.run();
                String[] cmds = new String[]{"sh", "-c", text};
                ProcessBuilder builder = new ProcessBuilder(cmds);
                builder.redirectErrorStream();

                try {
                    mProcess = builder.start();
                    is = mProcess.getInputStream();
                    err = mProcess.getErrorStream();

                    read(is, err);
                } catch (IOException e) {
                    e.printStackTrace();
                    println(text + ": error " + e.getMessage());
                }
            }
        };
        scriptShell.start();
    }


    private void read(InputStream inputStream, InputStream errorStream) {
        if (normal != null && normal.isAlive()) {
            normal.interrupt();
            normal = null;
        }
        if (error != null && error.isAlive()) {
            error.interrupt();
            error = null;
        }

        normal = new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    InputStreamReader isr = new InputStreamReader(inputStream);
                    BufferedReader mReader = new BufferedReader(isr);

                    String string;
                    while ((string = mReader.readLine()) != null) {
                        println(string);
                    }
                } catch (Exception e) {
                }
            }
        };

        error = new Thread() {
            @Override
            public void run() {
                super.run();

                try {
                    InputStreamReader isr = new InputStreamReader(errorStream);
                    BufferedReader mReader = new BufferedReader(isr);

                    String string;
                    while ((string = mReader.readLine()) != null) {
                        println(string);
                    }
                } catch (Exception e) {
                }
            }
        };
        if (inputStream != null) {
            normal.start();
        }
        if (errorStream != null) {
            error.start();
        }
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        Log.d(TAG, "onEditorAction: " + actionId + " " + event);
        if (actionId == EditorInfo.IME_ACTION_GO) {
            onClick();
            return true;
        }

        if (event != null) {
            if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER || event.getKeyCode() == KeyEvent.KEYCODE_SEARCH) {
                if (event.getAction() == 1) {
                    onClick();
                    return true;
                }
            }
        }
        return true;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 100) {
            requestPermission(true);
        }
    }

    private void requestPermission(boolean exit) {
        int w = checkSelfPermission(WRITE_EXTERNAL_STORAGE);
        int r = checkSelfPermission(READ_EXTERNAL_STORAGE);

        if (w != PackageManager.PERMISSION_GRANTED || r != PackageManager.PERMISSION_GRANTED) {
            if (!exit) {
                requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE}, 100);
            } else {
                Toast.makeText(this, "不要拒绝权限啊", Toast.LENGTH_LONG).show();
                System.exit(0);
            }
        }
    }
}

效果展示

image.png
image.png

使用

停止上次命令任务,输入:ctrl+c 或者其他有效命令

清屏内容:clear

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容