commit bf41e2f78c4b3ae6728cb41046c5eeab7c3c40da Author: Tushida <1161548209@qq.com> Date: Tue May 28 13:41:40 2024 +0800 完成的基本的页面布局,实现了通信功能 diff --git a/robot_android_App/APP/.idea/.gitignore b/robot_android_App/APP/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/robot_android_App/APP/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/robot_android_App/APP/.idea/APP.iml b/robot_android_App/APP/.idea/APP.iml new file mode 100644 index 0000000..18ec59d --- /dev/null +++ b/robot_android_App/APP/.idea/APP.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/.idea/modules.xml b/robot_android_App/APP/.idea/modules.xml new file mode 100644 index 0000000..b3a0693 --- /dev/null +++ b/robot_android_App/APP/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/Docs/Resource/robot.png b/robot_android_App/APP/Docs/Resource/robot.png new file mode 100644 index 0000000..3a6505b Binary files /dev/null and b/robot_android_App/APP/Docs/Resource/robot.png differ diff --git a/robot_android_App/APP/UPBot/.gitignore b/robot_android_App/APP/UPBot/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/robot_android_App/APP/UPBot/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/robot_android_App/APP/UPBot/.idea/.gitignore b/robot_android_App/APP/UPBot/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/robot_android_App/APP/UPBot/.idea/.name b/robot_android_App/APP/UPBot/.idea/.name new file mode 100644 index 0000000..6d90c9e --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/.name @@ -0,0 +1 @@ +WiFiDemo \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/.idea/compiler.xml b/robot_android_App/APP/UPBot/.idea/compiler.xml new file mode 100644 index 0000000..7d7ec2e --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/.idea/deploymentTargetDropDown.xml b/robot_android_App/APP/UPBot/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..ea59019 --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/.idea/gradle.xml b/robot_android_App/APP/UPBot/.idea/gradle.xml new file mode 100644 index 0000000..c5e57ce --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/.idea/migrations.xml b/robot_android_App/APP/UPBot/.idea/migrations.xml new file mode 100644 index 0000000..48052b2 --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/.idea/misc.xml b/robot_android_App/APP/UPBot/.idea/misc.xml new file mode 100644 index 0000000..c9eddb6 --- /dev/null +++ b/robot_android_App/APP/UPBot/.idea/misc.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/.gitignore b/robot_android_App/APP/UPBot/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/robot_android_App/APP/UPBot/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/build.gradle b/robot_android_App/APP/UPBot/app/build.gradle new file mode 100644 index 0000000..b3422f6 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'com.android.application' +} + +android { + compileSdk 31 + + defaultConfig { + applicationId "com.example.wifidemo" + minSdk 26 + targetSdk 31 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + testImplementation 'junit:junit:4.+' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/proguard-rules.pro b/robot_android_App/APP/UPBot/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/release/app-release.apk b/robot_android_App/APP/UPBot/app/release/app-release.apk new file mode 100644 index 0000000..dfcf09c Binary files /dev/null and b/robot_android_App/APP/UPBot/app/release/app-release.apk differ diff --git a/robot_android_App/APP/UPBot/app/release/output-metadata.json b/robot_android_App/APP/UPBot/app/release/output-metadata.json new file mode 100644 index 0000000..a1abaa9 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.example.wifidemo", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/androidTest/java/com/example/wifidemo/ExampleInstrumentedTest.java b/robot_android_App/APP/UPBot/app/src/androidTest/java/com/example/wifidemo/ExampleInstrumentedTest.java new file mode 100644 index 0000000..442aa6e --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/androidTest/java/com/example/wifidemo/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.wifidemo; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.wifidemo", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/AndroidManifest.xml b/robot_android_App/APP/UPBot/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..28715bd --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ClientActivity.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ClientActivity.java new file mode 100644 index 0000000..c68e327 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ClientActivity.java @@ -0,0 +1,171 @@ +package com.example.wifidemo; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; + +public class ClientActivity extends AppCompatActivity { + + private EditText ipET; + private EditText msgET; + private Button confirmBtn; + private Button sendBtn; + private ActionBar actionBar; + private Socket mSocket; +// private OutputStream mOutStream; +// private InputStream mInStream; + private SocketConnectThread socketConnectThread; + private StringBuffer stringBuffer = new StringBuffer(); + private TextView msgTV; + + private final String TAG="WifiDemoLogClientActivity"; + + @SuppressLint("HandlerLeak") + public Handler handler = new Handler(Looper.myLooper()){ + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + if (msg.what == 1){ + stringBuffer.append(msg.obj); + stringBuffer.append("\n"); + msgTV.setText(stringBuffer.toString()); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_client); + actionBar = getSupportActionBar(); + assert actionBar != null; + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle("机器人控制"); + + socketConnectThread = new SocketConnectThread(); + initView(); + setListener(); + } + + private void initView() { + ipET = (EditText) findViewById(R.id.ipET); + msgET = (EditText) findViewById(R.id.msgET); + sendBtn = findViewById(R.id.btn_send); + confirmBtn = findViewById(R.id.btn_confirm); + msgTV = (TextView) findViewById(R.id.msgTV); + } + + private void setListener() { + sendBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(mSocket==null) { + Toast.makeText(ClientActivity.this, "未进行连接", Toast.LENGTH_SHORT).show(); + return; + } + sendMessage(msgET.getText().toString()); + } + }); + confirmBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + socketConnectThread.start(); + confirmBtn.setEnabled(false);//连接只点一次 + } + }); + } + + /** + * 连接线程 + */ + class SocketConnectThread extends Thread{ + public void run(){ + try { + //指定ip地址和端口号 + mSocket = new Socket(ipET.getText().toString(), 1989); + //获取输出流、输入流 +// mOutStream = mSocket.getOutputStream(); +// mInStream = mSocket.getInputStream(); + } catch (Exception e) { + e.printStackTrace(); + return; + } + startReader(mSocket); + } + + } + + /** + * 发送消息 + * @param msg + */ + public void sendMessage(final String msg) { + if (msg.length() == 0){ + return; + } + new Thread() { + @Override + public void run() { + try { + DataOutputStream writer = new DataOutputStream(mSocket.getOutputStream()); + writer.writeUTF(msg); // 写一个UTF-8的信息 + } catch (IOException e) { + e.printStackTrace(); + } + } + }.start(); + } + + /** + * 接收消息 + */ + private void startReader(final Socket socket) { + new Thread(){ + @Override + public void run() { + DataInputStream reader; + try { + // 获取读取流 + reader = new DataInputStream(socket.getInputStream()); + while (true) { + // 读取数据 + String msg = reader.readUTF(); + Message message = new Message(); + message.what = 1; + message.obj=msg; + handler.sendMessage(message); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }.start(); + } + + @Override + protected void onDestroy() { + if(mSocket!=null){ + try { + mSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + super.onDestroy(); + } +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ESP8266ClientActivity.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ESP8266ClientActivity.java new file mode 100644 index 0000000..ced5a1f --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ESP8266ClientActivity.java @@ -0,0 +1,152 @@ +package com.example.wifidemo; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class ESP8266ClientActivity extends AppCompatActivity { + + private String mIp;//硬件的IP + private int mPort = 5000;//硬件的端口 + private EditText et_ip;//输入硬件对应的IP + private EditText et_msg;//输入要发送的消息 + private Button btn_confirm;//进行连接 + private Button btn_send;//发送消息 +// Socket mSocket = null;//连接成功可得到的Socket +// OutputStream outputStream = null;//定义输出流 +// InputStream inputStream = null;//定义输入流 + private StringBuffer sb = new StringBuffer();//消息 + private TextView tv_msg;//显示消息 + private boolean connectFlage = true;//连接成功或连接3s后变false + private TextView connetStatusTextView;//显示连接状态 + private int ShowPointSum = 0;//连接时显示 连接中.. 后面点的计数 + + private final String TAG = "WifiDemoLogESP8266ClientActivity"; + private LocalBroadcastManager localBroadcastManager;//本地广播管理器 + private MyLocalBroadcastReceiver localBroadcastReceiver;//广播接收者 + private int connectingCount=0;//用来刷新 正在连接 与 正 在 连 接 + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_esp8266_client); + initView();//初始化控件 + setListener();//设置Button的点击事件 + registerBroadcastReceiver();//广播注册 + } + + /** + * 初始化控件 + */ + private void initView() { + et_ip = (EditText) findViewById(R.id.et_ipESP8266); + et_msg = (EditText) findViewById(R.id.et_msgESP8266); + btn_send = findViewById(R.id.btn_sendESP8266); + btn_confirm = findViewById(R.id.btn_confirmESP8266); + tv_msg = (TextView) findViewById(R.id.tv_msgESP8266); + connetStatusTextView = findViewById(R.id.connetStatusTV); + } + + /** + * 设置Button的点击事件 + */ + private void setListener() { + btn_confirm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + btn_confirm.setEnabled(false);//防止正在连接时再次点击连接 + WiFiModeUtil.connectFlage=true; + mIp = et_ip.getText().toString();//得到IP + WiFiModeUtil.connetByTCP(mIp,mPort);//进行连接 + } + }); + btn_send.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (WiFiModeUtil.mSocket == null) { + Toast.makeText(ESP8266ClientActivity.this, "未连接任何设备~~", Toast.LENGTH_SHORT).show(); + return; + } + WiFiModeUtil.sendData(et_msg.getText().toString());//发送数据 + } + }); + } + + + @Override + protected void onDestroy() { + localBroadcastManager.unregisterReceiver(localBroadcastReceiver);//注销广播 + WiFiModeUtil.closeSocketAndStream();//关闭Socket释放资源 + super.onDestroy(); + } + + /** + * 广播注册 + */ + private void registerBroadcastReceiver() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction("WiFiModeUtil.Connecting");//正在连接 + intentFilter.addAction("WiFiModeUtil.Connect.Succeed");//连接成功 + intentFilter.addAction("WiFiModeUtil.Connect.Fail");//连接失败 + intentFilter.addAction("WiFiModeUtil.Connect.ReceiveMessage");//接收到数据 + intentFilter.addAction("WiFiModeUtil.Disconnected");//接收到数据 + localBroadcastReceiver = new MyLocalBroadcastReceiver(); + localBroadcastManager = LocalBroadcastManager.getInstance(this); + WiFiModeUtil.localBroadcastManager=localBroadcastManager;//给WiFiModeUtil工具类中的本地广播管理器赋值 + localBroadcastManager.registerReceiver(localBroadcastReceiver,intentFilter); + } + + /** + * 本地广播接收者 + */ + class MyLocalBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case "WiFiModeUtil.Connecting": + connectingCount++; + if(connectingCount%2==0) + connetStatusTextView.setText("正在连接"); + else + connetStatusTextView.setText("正 在 连 接"); + break; + case "WiFiModeUtil.Connect.Succeed": + connetStatusTextView.setText("连接成功"); + btn_confirm.setEnabled(true); + break; + case "WiFiModeUtil.Connect.Fail": + connetStatusTextView.setText("连接失败"); + btn_confirm.setEnabled(true); + break; + case "WiFiModeUtil.Connect.ReceiveMessage": + tv_msg.setText(WiFiModeUtil.DataRecivice.toString()); + break; + case "WiFiModeUtil.Disconnected": + tv_msg.setText("连接已断开,请重新进行连接"); + break; + } + } + } +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/MainActivity.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/MainActivity.java new file mode 100644 index 0000000..4a0b0c5 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/MainActivity.java @@ -0,0 +1,76 @@ +package com.example.wifidemo; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +public class MainActivity extends AppCompatActivity{ + + private ActionBar actionBar; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + actionBar = getSupportActionBar(); + assert actionBar != null; + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle("深安割草机器人"); + + findViewById(R.id.base_config_layout).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent=new Intent(MainActivity.this, ServiceActivity.class); + startActivity(intent); + } + }); + + findViewById(R.id.mowing_layout).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent=new Intent(MainActivity.this,ClientActivity.class); + startActivity(intent); + } + }); + + + findViewById(R.id.client_mode_layout).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent=new Intent(MainActivity.this,ESP8266ClientActivity.class); + startActivity(intent); + } + }); + + } + + + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == android.R.id.home){ + Toast.makeText(this,"返回上一页",Toast.LENGTH_LONG).show(); + finish(); + }else if (item.getItemId() == R.id.action_more){ + Toast.makeText(this,"更多设置",Toast.LENGTH_LONG).show(); + } + return true; + } + +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ServiceActivity.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ServiceActivity.java new file mode 100644 index 0000000..dee5cd3 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/ServiceActivity.java @@ -0,0 +1,205 @@ +package com.example.wifidemo; + +import androidx.appcompat.app.AppCompatActivity; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class ServiceActivity extends AppCompatActivity { + + private Button clearBtn; + private Button showIPBtn; + private TextView ipTV; + private TextView msgTV; + private ServerSocket mServerSocket; + private Socket mSocket; + private StringBuffer stringBuffer = new StringBuffer(); + private final String TAG = "WifiDemoLogServiceActivity"; + + + @SuppressLint("HandlerLeak") + public Handler handler = new Handler(Looper.myLooper()) { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + if (msg.what == 1) { + stringBuffer.append(msg.obj); + stringBuffer.append("\n"); + msgTV.setText(stringBuffer.toString()); + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_service); + initView(); + setListener(); + + try { + mServerSocket = new ServerSocket(5000);//端口号5000 + } catch (IOException e) { + e.printStackTrace(); + } + //启动服务线程 + SocketAcceptThread socketAcceptThread = new SocketAcceptThread(); + socketAcceptThread.start(); + } + + /** + * 获得控件实例 + */ + private void initView() { + clearBtn = findViewById(R.id.clearBtn); + showIPBtn = findViewById(R.id.showIPBtn); + ipTV = findViewById(R.id.ipTV); + msgTV = findViewById(R.id.msgTV); + } + + /** + * 为控件设置监听 + */ + private void setListener() { + clearBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stringBuffer.setLength(0); + msgTV.setText(""); + } + }); + showIPBtn.setOnClickListener(new View.OnClickListener() { + @SuppressLint("SetTextI18n") + @Override + public void onClick(View v) { + ipTV.setText(getLocalIpAddress(ServiceActivity.this) + ":5000"); + } + }); + + } + + + /** + * 连接线程 + * 得到Socket + */ + class SocketAcceptThread extends Thread { + @Override + public void run() { + try { + //等待客户端的连接,Accept会阻塞,直到建立连接, + //所以需要放在子线程中运行 + mSocket = mServerSocket.accept(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + //启动消息接收线程 + startReader(mSocket); + } + } + + /** + * 从参数的Socket里获取最新的消息 + */ + private void startReader(final Socket socket) { + + new Thread() { + @Override + public void run() { + DataInputStream reader; + try { + // 获取读取流 + reader = new DataInputStream(socket.getInputStream()); + while (true) { + // 读取数据 + String msg = reader.readUTF(); + Log.d(TAG, "客户端的信息:" + msg); + + //告知客户端消息收到 + DataOutputStream writer = new DataOutputStream(mSocket.getOutputStream()); + writer.writeUTF("收到:" + msg); // 写一个UTF-8的信息 + + //发消息更新UI + Message message = new Message(); + message.what = 1; + message.obj=msg; + handler.sendMessage(message); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }.start(); + } + + @Override + protected void onDestroy() { + if (mServerSocket != null) { + try { + mServerSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if(mSocket!=null){ + try { + mSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + super.onDestroy(); + } + + /** + * 将ip的整数形式转换成ip形式 + * + * @param ipInt + * @return + */ + public static String int2ip(int ipInt) { + StringBuilder sb = new StringBuilder(); + sb.append(ipInt & 0xFF).append("."); + sb.append((ipInt >> 8) & 0xFF).append("."); + sb.append((ipInt >> 16) & 0xFF).append("."); + sb.append((ipInt >> 24) & 0xFF); + return sb.toString(); + } + + /** + * 获取当前ip地址 + * + * @param context + * @return + */ + public static String getLocalIpAddress(Context context) { + try { + + WifiManager wifiManager = (WifiManager) context + .getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + int i = wifiInfo.getIpAddress(); + return int2ip(i); + } catch (Exception ex) { + return " 获取IP出错鸟!!!!请保证是WIFI,或者请重新打开网络!\n" + ex.getMessage(); + } + // return null; + } +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/SplashActivity.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/SplashActivity.java new file mode 100644 index 0000000..9aa5b0a --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/SplashActivity.java @@ -0,0 +1,57 @@ +package com.example.wifidemo; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; + +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatActivity; + +@SuppressLint("CustomSplashScreen") +public class SplashActivity extends AppCompatActivity { + + @RequiresApi(api = Build.VERSION_CODES.P) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + fullScreenDisplay(); + setContentView(R.layout.activity_splash); + //创建子线程 + Thread mThread=new Thread(){ + @Override + public void run() { + super.run(); + try { + sleep(3000);//使程序休眠3秒 + Intent intent=new Intent(getApplicationContext(),MainActivity.class); + startActivity(intent); + finish(); + }catch (Exception e){ + e.printStackTrace(); + } + } + }; + mThread.start();//启动线程 + } + @RequiresApi(api = Build.VERSION_CODES.P) + protected void fullScreenDisplay(){ + // 隐藏状态栏和ActionBar,实现全屏画面的效果 + //隐藏状态栏 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + //隐藏标题栏 + getSupportActionBar().hide(); + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN); + + WindowManager.LayoutParams lp = getWindow().getAttributes(); + + // 始终允许窗口延伸到屏幕短边上的刘海区域 + lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams. + LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + + getWindow().setAttributes(lp); + } +} \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/WiFiModeUtil.java b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/WiFiModeUtil.java new file mode 100644 index 0000000..26aec53 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/java/com/example/wifidemo/WiFiModeUtil.java @@ -0,0 +1,260 @@ +package com.example.wifidemo; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * 正在连接广播Action:"WiFiModeUtil.Connecting" + * 连接失败广播Action:"WiFiModeUtil.Connect.Fail" + * 连接成功广播Action:"WiFiModeUtil.Connect.Succeed" + * 收到数据广播Action:"WiFiModeUtil.Connect.ReceiveMessage" + * 连接断开广播Action:"WiFiModeUtil.Disconnected" + */ +public class WiFiModeUtil { + private static String mIp;//硬件的IP + private static int mPort = 5000;//硬件的端口 + public static Socket mSocket = null;//连接成功可得到的Socket + public static OutputStream outputStream = null;//定义输出流 + public static InputStream inputStream = null;//定义输入流 + public static StringBuffer DataRecivice = new StringBuffer();//数据 + public static List DataList=new ArrayList<>();//数据 + public static boolean connectFlage = true;//连接成功或连接3s后变false + // private static int ShowPointSum = 0;//连接时显示 连接中.. 后面点的计数 + private static final String TAG = "WifiDemoLogESP8266ClientActivity"; + + /** + * 本地广播管理器 从外面用: + * WiFiModeUtil.localBroadcastManager=localBroadcastManager; + * 进行赋值 + */ + public static LocalBroadcastManager localBroadcastManager; + + /** + * 处理消息的Handler + */ + @SuppressLint("HandlerLeak") + public static Handler mHandler = new Handler(Looper.myLooper()) { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case 0://接收到数据 + DataRecivice.append(msg.obj.toString()); + DataRecivice.append("\n"); + DataList.add(msg.obj.toString());//添加数据 + Intent intent = new Intent("WiFiModeUtil.Connect.ReceiveMessage"); + localBroadcastManager.sendBroadcast(intent);//发送收到数据广播 + break; + case 1://连接成功 + Intent intent2 = new Intent("WiFiModeUtil.Connect.Succeed"); + localBroadcastManager.sendBroadcast(intent2);//发送连接成功广播 + readData();//开启接收线程 + connectFlage = true; + break; + case 2://连接断开 + Intent intent3 = new Intent("WiFiModeUtil.Disconnected"); + localBroadcastManager.sendBroadcast(intent3);//发送连接失败广播 + connectFlage = true; + break; + } + } + }; + + /*** + * 延时3s的定时器 + * 在开始连接时计时3s + * 3s未连接上视为连接失败 + */ + private final static CountDownTimer tcpClientCountDownTimer = new CountDownTimer(3000, 300) { + @Override + public void onTick(long millisUntilFinished) {//每隔300ms进入 + if (connectFlage) { + Intent intent = new Intent("WiFiModeUtil.Connecting"); + localBroadcastManager.sendBroadcast(intent); + } + } + + @Override + public void onFinish() {//3s后进入(没有取消定时器的情况下) + if (connectFlage) { + connectFlage = false;//连接失败 + closeSocketAndStream(); + } + tcpClientCountDownTimer.cancel();//关掉定时器 + Intent intent = new Intent("WiFiModeUtil.Connect.Fail"); + localBroadcastManager.sendBroadcast(intent); + Log.d(TAG,"连接失败"); + } + }; + + /** + * 关掉Socket和输入输出流 + */ + public static void closeSocketAndStream() { + if (outputStream != null) { + try { + outputStream.close(); + Log.d(TAG,"关闭输出流"); + } catch (IOException e) { + e.printStackTrace(); + } + outputStream = null; + } + if (inputStream != null) { + try { + inputStream.close(); + Log.d(TAG,"关闭输入流"); + } catch (IOException e) { + e.printStackTrace(); + } + inputStream = null; + } + if (mSocket != null) { + try { + mSocket.close(); + Log.d(TAG,"关闭Socket"); + } catch (IOException e) { + e.printStackTrace(); + } + mSocket = null; + } + } + + /** + * 连接服务器任务 + */ + static class ConnectSeverThread extends Thread { + @Override + public void run() { + Log.d(TAG,"连接线程开启"); + while (connectFlage) { + try { + Log.d(TAG,"正在连接..."); + mSocket = new Socket(mIp, mPort);//进行连接 + connectFlage = false;//已连接 + tcpClientCountDownTimer.cancel();//关掉计时器 + /*连接成功更新显示连接状态的UI*/ + Message msg = new Message(); + msg.what = 1; + mHandler.sendMessage(msg); + Log.d(TAG,"连接成功"); + inputStream = mSocket.getInputStream();//获取输入流 + Log.d(TAG,"获取输入流"); + outputStream = mSocket.getOutputStream();////获取输出流 + Log.d(TAG,"获取输出流"); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + Log.d(TAG,"连接过程中出错"); + } + } + } + } + + /** + * 传入硬件服务端IP建立TCP连接 + * 正在连接每200ms 发送一条‘正在连接’广播 + * 3秒后还未连接则连接失败 发送一条‘失败广播’ + * 连接成功会 发送一条‘成功广播’ + * + * @param IPAdress + */ + public static void connetByTCP(String IPAdress,int Port) { + mIp = IPAdress; + mPort=Port; + ConnectSeverThread connectSeverThread = new ConnectSeverThread(); + connectSeverThread.start(); + tcpClientCountDownTimer.start(); + } + + /** + * 向硬件发送数据 + */ + public static void sendData(String data) { + if(mSocket!=null){ + byte[] sendByte = data.getBytes(); + new Thread() { + @Override + public void run() { + try { + Log.d(TAG,"发送线程开启 正在发送中..."); + DataOutputStream writer = new DataOutputStream(outputStream); + writer.write(sendByte, 0, sendByte.length); + Log.d(TAG,"已发送数据:"+data); + } catch (IOException e) { + e.printStackTrace(); + } + Log.d(TAG,"发送线程结束"); + } + }.start(); + } + } + + /** + * 连接成功后开启 + * 接收硬件发送的数据 + */ + public static void readData() { + new Thread() { + @Override + public void run() { + Log.d(TAG,"接收线程开启"); + try { + while (true) { + Thread.sleep(200); + //如果连接断开 尝试重连 + try { + /* + sendUrgentData()方法 + 它会往输出流发送一个字节的数据, + 只要对方Socket的SO_OOBINLINE属性没有打开, + 就会自动舍弃这个字节, + 就会抛出异常, + 而SO_OOBINLINE属性默认情况下就是关闭的 + */ + mSocket.sendUrgentData(0xFF);//发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信 + } catch (Exception ex) { + Log.d(TAG,"连接已断开,请重新进行连接"); + Message msg=new Message(); + msg.what=2; + msg.obj="连接已断开,请重新进行连接"; + mHandler.sendMessage(msg); + } + DataInputStream reader = new DataInputStream(inputStream); + byte[] buffer = new byte[1024]; + int len; + while ((len = reader.read(buffer)) != -1) { + String data = new String(buffer, 0, len); + Log.d(TAG,"接收到数据:"+data); + Message msg = new Message(); + msg.what = 0; + msg.obj = data; + mHandler.sendMessage(msg); + } + } + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + Log.d(TAG,"接收线程结束"); + } + }.start(); + } +} diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/background.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/background.png new file mode 100644 index 0000000..c8fca3b Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/background.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/backpt.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/backpt.png new file mode 100644 index 0000000..4de6fb9 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/backpt.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/base_station.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/base_station.png new file mode 100644 index 0000000..f6d5edc Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/base_station.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/baseline_more_24.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable/baseline_more_24.xml new file mode 100644 index 0000000..bffcb2a --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable/baseline_more_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/battery.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/battery.png new file mode 100644 index 0000000..298b1d6 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/battery.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/button.jpeg b/robot_android_App/APP/UPBot/app/src/main/res/drawable/button.jpeg new file mode 100644 index 0000000..ad445dd Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/button.jpeg differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher.png new file mode 100644 index 0000000..5b5c92c Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher_background.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/icon_dot.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/icon_dot.png new file mode 100644 index 0000000..484c1ca Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/icon_dot.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/logo.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/logo.png new file mode 100644 index 0000000..581e08e Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/logo.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowgin_working.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowgin_working.png new file mode 100644 index 0000000..243b7b0 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowgin_working.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowing_mapping.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowing_mapping.png new file mode 100644 index 0000000..e7a1b83 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/mowing_mapping.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/path_plan.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/path_plan.png new file mode 100644 index 0000000..47d06a3 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/path_plan.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_charge.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_charge.png new file mode 100644 index 0000000..5176ae2 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_charge.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_medium.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_medium.png new file mode 100644 index 0000000..1090c58 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_medium.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_patrol.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_patrol.png new file mode 100644 index 0000000..ba62c73 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_patrol.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_start.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_start.png new file mode 100644 index 0000000..da6745c Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/robot_start.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/rounded_corner_bg.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable/rounded_corner_bg.xml new file mode 100644 index 0000000..78ab88d --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable/rounded_corner_bg.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/satellite.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/satellite.png new file mode 100644 index 0000000..ea4293b Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/satellite.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/setting.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/setting.png new file mode 100644 index 0000000..4d9a36c Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/setting.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_oval_rose.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_oval_rose.xml new file mode 100644 index 0000000..43c268b --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_oval_rose.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_rect_gold.xml b/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_rect_gold.xml new file mode 100644 index 0000000..ae07130 --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/drawable/shape_rect_gold.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/target.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/target.png new file mode 100644 index 0000000..6b8a493 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/target.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/upbot_logo.jpg b/robot_android_App/APP/UPBot/app/src/main/res/drawable/upbot_logo.jpg new file mode 100644 index 0000000..f3e28ba Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/upbot_logo.jpg differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb.png new file mode 100644 index 0000000..098879f Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_base_blue.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_base_blue.png new file mode 100644 index 0000000..52ac1c0 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_base_blue.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_battery.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_battery.png new file mode 100644 index 0000000..de1f677 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/uwb_battery.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/drawable/wifi.png b/robot_android_App/APP/UPBot/app/src/main/res/drawable/wifi.png new file mode 100644 index 0000000..1bdb6d3 Binary files /dev/null and b/robot_android_App/APP/UPBot/app/src/main/res/drawable/wifi.png differ diff --git a/robot_android_App/APP/UPBot/app/src/main/res/layout/activity_client.xml b/robot_android_App/APP/UPBot/app/src/main/res/layout/activity_client.xml new file mode 100644 index 0000000..99fc5ba --- /dev/null +++ b/robot_android_App/APP/UPBot/app/src/main/res/layout/activity_client.xml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +