实现热点列表

main
Matthew 12 months ago
parent 68e81438dd
commit dcb0a180b2

@ -9,8 +9,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
@ -24,12 +24,18 @@
android:theme="@style/Theme.MpRemote"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".SettingsActivity"
android:label="@string/title_activity_settings"
android:exported="false" />
<activity
android:name=".DeviceActivity"
android:label="@string/title_activity_device"
android:exported="false" />
<activity
android:name=".VideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/title_activity_video"
android:exported="false" />
<activity
android:name=".WifiScanActivity"
@ -47,8 +53,7 @@
android:name=".ImageActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="false"
android:label="@string/title_activity_image"
/>
android:label="@string/title_activity_image" />
</application>
</manifest>

@ -5,6 +5,7 @@ import androidx.appcompat.app.AppCompatActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.net.wifi.WifiManager;
@ -82,6 +83,7 @@ public class DeviceActivity extends AppCompatActivity {
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
// actionBar.setTitle(R.string.title_activity_device);
Intent intent = getIntent();
mDeviceIp = intent.getStringExtra("deviceIp");
@ -117,7 +119,12 @@ public class DeviceActivity extends AppCompatActivity {
ex.printStackTrace();
}
mProgressDialog.dismiss();
mHandler.post(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
}
});
}
private void runImpl() {
@ -167,6 +174,7 @@ public class DeviceActivity extends AppCompatActivity {
if (pos != -1) {
String val = line.substring(pos + 12);
mProps.put(KEY_APP_MP_VERSION, val);
break;
}
}
}
@ -256,10 +264,15 @@ public class DeviceActivity extends AppCompatActivity {
}
});
mProgressDialog = ProgressDialog.show(context, "", "连接中", true);
mProgressDialog = ProgressDialog.show(this, "", "连接中", true, true);
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
DeviceActivity.this.finish();
}
});
}
protected boolean pullFile(Dadb adb, String remoteFilePath, File localFilePath) {
if (localFilePath.exists()) {
localFilePath.delete();
@ -427,8 +440,9 @@ public class DeviceActivity extends AppCompatActivity {
}
private void showPhoto(final String filePath) {
Intent intent = new Intent(DeviceActivity.this, ImageActivity.class);
Intent intent = new Intent(this, ImageActivity.class);
intent.putExtra("path", filePath);
intent.putExtra("cmdid", TextUtils.isEmpty(mAppConfig.cmdid) ? "" : mAppConfig.cmdid);
// intent.putExtra("info", info);
startActivity(intent);
}

@ -1,37 +1,54 @@
package com.xypower.mpremote;
import androidx.appcompat.app.ActionBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkSpecifier;
import android.net.wifi.WifiNetworkSuggestion;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import com.xypower.common.FileUtils;
import com.xypower.common.MicroPhotoContext;
import com.xypower.mpremote.databinding.ActivityMainBinding;
import java.io.File;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dadb.AdbKeyPair;
import dadb.AdbShellResponse;
import dadb.Dadb;
public class MainActivity extends AppCompatActivity {
// Used to load the 'mpremote' library on application startup.
@ -41,38 +58,57 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "ADB";
private static final boolean DEBUG = false;
private static final String PACKAGE_NAME_MP = "com.xypower.mpapp";
private static final String REMOTE_PATH_ROOT = "/sdcard/" + PACKAGE_NAME_MP + "/";
private static final String REMOTE_PATH_DATA = REMOTE_PATH_ROOT + "data/";
private static final String REMOTE_PATH_PHOTOS = REMOTE_PATH_ROOT + "photos/";
private static final String REMOTE_PATH_TMP = REMOTE_PATH_ROOT + "tmp/";
private static final boolean DEBUG = true;
private static final String KEY_APP_BV = "app.bv";
private static final String KEY_APP_BCV = "app.bcv";
private static final String KEY_RO_SERIALNO = "ro.serialno";
public static final int RSSI_LEVELS = 5;
private static final String KEY_APP_MP_VERSION = "mpapp.version";
private static final String DEFAULT_PRE_SHARED_KEY = SettingsActivity.DEFAULT_PRE_SHARED_KEY;
private static final int ADB_SERVER_PORT = 5555;
private static final int REQUEST_CODE_PERMISSIONS = 1025;
private static final int REQUEST_CODE_SETTINGS = SettingsActivity.REQUEST_CODE_SETTINGS;
private static final String WIFI_IP_PREFIX = "192.168.";
// private static final String WIFI_IP_DEVIDE = "192.168.50.1";
private static final String WIFI_IP_DEVICE = "192.168.50.137";
// [vendor.ril.nw.signalstrength.lte.1]: [-85,27]
//[vendor.ril.nw.signalstrength.lte.2]: [-86,27]
private static final String WIFI_IP_DEVICE = "192.168.50.92";
private ActivityMainBinding binding;
private Handler mHandler;
private Dadb mAdb;
private Map<String, String> mProps = new HashMap<>();
private List<ScanResult> mScanResults = null;
private ScanResult mCurrentScanResult;
private String mPassword = DEFAULT_PRE_SHARED_KEY;
private WifiAdaper mAdapter;
private List<Map<String, Object>> mItems = new ArrayList<Map<String, Object>>();
private MicroPhotoContext.AppConfig mAppConfig;
private int mBatteryVoltage = -1;
private int mBatteryChargingVoltage = -1;
private ConnectivityManager mConnectivityManager;
private NetworkConnectChangedReceiver mNetworkConnectChangedReceiver;
private ConnectivityManager.NetworkCallback mNetworkCallback;
public class WifiAdaper extends SimpleAdapter{
public WifiAdaper(Context context, List<Map<String, Object>> items, int resource, String[] from, int[] to) {
super(context, items, resource, from, to);
}
public View getView(int position, View convertView, ViewGroup parent){
convertView = super.getView(position, convertView, parent);
TextView textView = (TextView)convertView.findViewById(R.id.id_text);
ScanResult scanResult = mScanResults.get(position);
// if (scanResult.)
String text = (String)mItems.get(position).get("text");
if (text.startsWith("XY") || text.startsWith("xy")) {
textView.setTextColor(Color.RED);
textView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
} else {
textView.setTextColor(Color.BLACK);
textView.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
}
return convertView;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -81,433 +117,446 @@ public class MainActivity extends AppCompatActivity {
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
loadSettings();
mHandler = new Handler();
File file = new File(getFilesDir(), ".keypair");
if (!file.exists()) {
file.mkdirs();
}
final File pubKeyFile = new File(file, "pub.key");
final File priKeyFile = new File(file, "pri.key");
if (!priKeyFile.exists() || !pubKeyFile.exists()) {
AdbKeyPair.generate(priKeyFile, pubKeyFile);
}
mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
final File fileTmp = new File(getFilesDir(), "tmp");
if (!fileTmp.exists()) {
fileTmp.mkdirs();
}
final AdbKeyPair adbKeyPair = AdbKeyPair.read(priKeyFile, pubKeyFile);
final Context context = getApplicationContext();
(new Thread(new Runnable() {
binding.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void run() {
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
String ipAddressByWifi = null;
if (wifiManager != null) {
ipAddressByWifi = Formatter.formatIpAddress(wifiManager.getDhcpInfo().ipAddress);
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
if (ipAddressByWifi.contains(WIFI_IP_PREFIX)) {
mCurrentScanResult = mScanResults.get(position);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String s1 = normalizeSSID(mCurrentScanResult.SSID);
String s2 = normalizeSSID(wifiInfo.getSSID());
if (TextUtils.equals(s1, s2)) {
openDeviceActivity();
} else {
startActivity(new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS));
}
// connectWifi(mCurrentScanResult.SSID, mCurrentScanResult.BSSID, mPassword);
}
});
if (applyForPermissions()) {
scanWifi();
}
}
String deviceIP = Formatter.formatIpAddress(wifiManager.getDhcpInfo().gateway);
if (DEBUG) {
deviceIP = WIFI_IP_DEVICE;
}
@Override
protected void onDestroy() {
super.onDestroy();
Socket mSocket = null;
try {
mSocket = new Socket(deviceIP, ADB_SERVER_PORT);
if (mSocket.isConnected()) {
mSocket.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
if (mConnectivityManager != null && mNetworkCallback != null) {
mConnectivityManager.bindProcessToNetwork(null);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
}
Dadb adb = Dadb.discover(deviceIP, adbKeyPair);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
return true;
}
if (adb != null) {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//当点击不同的menu item 是执行不同的操作
switch (id) {
case R.id.action_refresh:
scanWifi();
break;
case R.id.action_settings: {
Intent intent = new Intent(getApplicationContext(), SettingsActivity.class);
startActivityForResult(intent, REQUEST_CODE_SETTINGS);
}
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
mAdb = adb;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
try {
ArrayList<String> requestList = new ArrayList<>();//允许询问列表
ArrayList<String> banList = new ArrayList<>();//禁止列表
for(int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG,"【"+permissions[i]+"】权限授权成功");
} else {
//判断是否允许重新申请该权限
boolean nRet = ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[i]);
Log.i(TAG,"shouldShowRequestPermissionRationale nRet="+nRet);
if (nRet) {//允许重新申请
requestList.add(permissions[i]);
} else {//禁止申请
banList.add(permissions[i]);
}
}
}
AdbShellResponse adbShellResponse = null;
try {
adbShellResponse = mAdb.shell("getprop");
} catch (Exception ex) {
ex.printStackTrace();
}
//优先对禁止列表进行判断
if (banList.size() > 0) {
//告知该权限作用,要求手动授予权限
showFinishedDialog();
}
else if (requestList.size() > 0) {
//告知权限的作用,并重新申请
showTipDialog(requestList);
} else {
// showToast("权限授权成功");
scanWifi();
}
} catch (Exception e) {
e.printStackTrace();
// showToast("权限申请回调中发生异常");
}
}
if (adbShellResponse.getExitCode() == 0) {
String[] lines = FileUtils.splitLines(adbShellResponse.getAllOutput());
for (String line : lines) {
int pos = line.indexOf("]: [");
if (pos != -1) {
String key = line.substring(1, pos);
String val = line.substring(pos + 4, line.length() - 1);
mProps.put(key, val);
}
}
}
public void showFinishedDialog() {
AlertDialog dialog = new AlertDialog.Builder(getApplicationContext())
.setTitle("警告")
.setMessage("请前往设置中打开相关权限,否则功能无法正常运行!")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 一般情况下如果用户不授权的话,功能是无法运行的,做退出处理
finish();
}
})
.create();
dialog.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// adb shell pm dump com.xypower.mpremote | grep "versionName"
adbShellResponse = null;
try {
adbShellResponse = mAdb.shell("pm dump " + PACKAGE_NAME_MP + " | grep \"versionName\"");
} catch (Exception ex) {
ex.printStackTrace();
}
if (requestCode == REQUEST_CODE_SETTINGS) {
loadSettings();
}
}
if (adbShellResponse.getExitCode() == 0) {
String[] lines = FileUtils.splitLines(adbShellResponse.getAllOutput());
for (String line : lines) {
int pos = line.indexOf("versionName=");
if (pos != -1) {
String val = line.substring(pos + 12);
mProps.put(KEY_APP_MP_VERSION, val);
}
}
}
public void showTipDialog(ArrayList<String> pmList){
AlertDialog dialog = new AlertDialog.Builder(getApplicationContext())
.setTitle("提示")
.setMessage("【"+pmList.toString()+"】权限为应用必要权限,请授权")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String[] sList=pmList.toArray(new String[0]);
//重新申请该权限
ActivityCompat.requestPermissions(MainActivity.this, sList, REQUEST_CODE_PERMISSIONS);
}
})
.create();
dialog.show();
}
String remoteFilePath = REMOTE_PATH_TMP + "bv.txt";
String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"query_bv\" --es path \"" + remoteFilePath + "\"";
adbShellResponse = null;
try {
adbShellResponse = mAdb.shell(cmd);
} catch (Exception ex) {
ex.printStackTrace();
}
if (adbShellResponse.getExitCode() == 0) {
File localFilePath = new File(fileTmp, "bv.txt");
if (localFilePath.exists()) {
localFilePath.delete();
}
protected void scanWifi() {
for (int idx = 0; idx < 10; idx++) {
boolean res = pullFile(mAdb, remoteFilePath, localFilePath);
if (res) {
String content = FileUtils.readTextFile(localFilePath.getAbsolutePath());
if (!TextUtils.isEmpty(content)) {
int pos = content.indexOf(" ");
if (pos != -1) {
String bv = content.substring(0, pos);
String bcv = content.substring(pos + 1);
if (!TextUtils.isEmpty(bv)) {
mBatteryVoltage = Integer.parseInt(bv);
}
if (!TextUtils.isEmpty(bcv)) {
mBatteryChargingVoltage = Integer.parseInt(bcv);
}
}
}
localFilePath.delete();
break;
}
mScanResults = null;
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
(new Thread(new Runnable() {
@Override
public void run() {
try {
mAdb.shell("rm " + remoteFilePath);
} catch (Exception ex) {
ex.printStackTrace();
}
}
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.startScan();
mScanResults = wifiManager.getScanResults();
File appConfigFile = new File(fileTmp, "App.json");
boolean res = pullFile(adb, REMOTE_PATH_DATA + "App.json", appConfigFile);
if (res) {
// mAdapter = new ArrayAdapter<>()
final MicroPhotoContext.AppConfig appConfig = MicroPhotoContext.getMpAppConfig(context, appConfigFile.getAbsolutePath());
mAppConfig = appConfig;
mHandler.post(new Runnable() {
@Override
public void run() {
refreshListView();
}
});
}
})).start();
}
MainActivity.this.mHandler.post(new Runnable() {
private void connectWifi(String ssid, String bssid, String pwd) {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
// Android 10
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
WifiNetworkSuggestion suggestion =
new WifiNetworkSuggestion.Builder()
.setSsid(ssid)
.setWpa2Passphrase((pwd))
.setIsAppInteractionRequired(true)
.build();
List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>();
suggestionsList.add(suggestion);
wifiManager.removeNetworkSuggestions(suggestionsList);
int status = wifiManager.addNetworkSuggestions(suggestionsList);
// step2
if ((status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) || (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE)) {
WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder()
.setSsid(ssid)
.setWpa2Passphrase(pwd)
.build();
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(wifiNetworkSpecifier)
.build();
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(@NonNull Network network) {
super.onAvailable(network);
NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String s1 = normalizeSSID(wifiInfo.getSSID());
String s2 = normalizeSSID(mCurrentScanResult.SSID);
if (TextUtils.equals(s1, s2)) {
runOnUiThread(new Runnable() {
@Override
public void run() {
MainActivity.this.showAppInfo();
// requires android.permission.INTERNET
mConnectivityManager.bindProcessToNetwork(network);
openDeviceActivity();
}
});
}
}
} else {
mHandler.post(new Runnable() {
@Override
public void onUnavailable() {
super.onUnavailable();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, R.string.err_dev_not_found, Toast.LENGTH_LONG).show();
mConnectivityManager.bindProcessToNetwork(null);
}
});
}
};
}
// Dadb dadb = Dadb.create("localhost", 5555, adbKeyPair);
// Dadb.discover();
mConnectivityManager.requestNetwork(request, mNetworkCallback);
} else {
}
})).start();
binding.takePhoto1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takePhoto(1, 255, true);
} else {
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = "\"" + ssid + "\"";
wifiConfiguration.BSSID = "\"" + bssid + "\"";
wifiConfiguration.preSharedKey = "\"" + pwd + "\"";
int nerworkId = wifiManager.addNetwork(wifiConfiguration);
try {
wifiManager.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
}
});
binding.takeVideo1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takeVideo(1, 255, false);
/*
if (mNetworkConnectChangedReceiver != null) {
unregisterReceiver(mNetworkConnectChangedReceiver);
mNetworkConnectChangedReceiver = null;
registerNetworkConnectChangeReceiver();
}
});
}
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
return true;
wifiManager.enableNetwork(nerworkId, true);
wifiManager.reconnect();
}
}
private void refreshListView() {
mAdapter = new WifiAdaper(this, getData(), R.layout.list_item, new String[] { "img", "text" },
new int[] { R.id.id_img, R.id.id_text });
protected boolean pullFile(Dadb adb, String remoteFilePath, File localFilePath) {
if (localFilePath.exists()) {
localFilePath.delete();
}
binding.listView.setAdapter(mAdapter);
Log.i(TAG, "PULL: " + remoteFilePath);
}
boolean res = false;
try {
adb.pull(localFilePath, remoteFilePath);
res = true;
} catch (Exception ex) {
ex.printStackTrace();
private List<Map<String, Object>> getData() {
mItems.clear();
for (ScanResult scanResult : mScanResults) {
Map map = new HashMap<String, Object>();
int signalLevel = WifiManager.calculateSignalLevel(scanResult.level, RSSI_LEVELS);
int drawableId = R.drawable.ic_wifi_signal_0;
switch (signalLevel) {
case 1:
drawableId = R.drawable.ic_wifi_signal_1;
break;
case 2:
drawableId = R.drawable.ic_wifi_signal_2;
break;
case 3:
drawableId = R.drawable.ic_wifi_signal_3;
break;
case 4:
drawableId = R.drawable.ic_wifi_signal_4;
break;
default:
drawableId = R.drawable.ic_wifi_signal_0;
break;
}
map.put("img", drawableId);
map.put("text", scanResult.SSID);
mItems.add(map);
}
return res && localFilePath.exists();
return mItems;
}
protected void takePhotoImpl(final String cmd, final int channel, final int preset, final String localFileName, final String remoteFilePath, final boolean photoOrVideo, long sleepTime) {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (Exception ex) {
}
class NetworkConnectChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
// 这个监听wifi的连接状态即是否连上了一个有效无线路由当上边广播的状态是WifiManager.WIFI_STATE_DISABLING和WIFI_STATE_DISABLED的时候根本不会接到这个广播。
// 在上边广播接到广播是WifiManager.WIFI_STATE_ENABLED状态的同时也会接到这个广播当然刚打开wifi肯定还没有连接到有效的无线
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
Parcelable parcelableExtra = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
WifiInfo wifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
String bssid = intent.getStringExtra(WifiManager.EXTRA_BSSID);
if (null == parcelableExtra) {
return;
}
AdbShellResponse adbShellResponse = null;
try {
adbShellResponse = mAdb.shell(cmd);
} catch (Exception ex) {
ex.printStackTrace();
if (mCurrentScanResult != null) {
return;
}
NetworkInfo networkInfo = (NetworkInfo) parcelableExtra;
NetworkInfo.State state = networkInfo.getState();
// Log.e("NetWork Sate Change:"+state+" connectedBssid:" + connectedBssid);
if(state == NetworkInfo.State.CONNECTED) {
String ssid = wifiInfo.getSSID();
String addSSID = mCurrentScanResult.SSID;
if (!(mCurrentScanResult.SSID.startsWith("\"") && mCurrentScanResult.SSID.endsWith("\""))) {
addSSID = "\"" + addSSID + "\"";
}
// Log.i(ssid + "***>" + mCurrentScanResult.SSID);
// Log.i("总共耗时:"+((System.currentTimeMillis()-start)/1000.0));
if (ssid.equals(addSSID)) {
if (adbShellResponse != null) {
if (adbShellResponse.getExitCode() == 0) {
File localFilePath = new File(getFilesDir(), localFileName);
for (int idx = 0; idx < 10; idx++) {
boolean res = pullFile(mAdb, remoteFilePath, localFilePath);
if (res) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (photoOrVideo) {
showPhoto(localFilePath.getAbsolutePath());
} else {
showVideo(localFilePath.getAbsolutePath());
}
}
});
break;
}
try {
Thread.sleep(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
}
unregisterReceiver(mNetworkConnectChangedReceiver);
mNetworkConnectChangedReceiver = null;
try {
// mAdb.shell("rm " + remoteFilePath);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
});
th.start();
}
protected long takePhoto(int channel, int preset, final boolean photoOrVideo) {
if (mAdb == null) {
return 0;
}
}
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
Date dt = new Date();
int cameraId = channel - 1;
final String fileName = "IMG-" + Integer.toString(channel) + "-" + simpleDateFormat.format(dt) + (photoOrVideo ? ".jpg" : ".mp4");
final String remoteFilePath = REMOTE_PATH_TMP + fileName;
String leftTopOsd = "";
if (mAppConfig != null) {
leftTopOsd += mAppConfig.cmdid + " ";
}
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
leftTopOsd += simpleDateFormat2.format(dt) + " ";
leftTopOsd += "CH:" + Integer.toString(channel) + " ";
private void registerNetworkConnectChangeReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
mNetworkConnectChangedReceiver = new NetworkConnectChangedReceiver();
registerReceiver(mNetworkConnectChangedReceiver, filter);
}
String usb = (channel == 4) ? "true" : "false";
final String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"take_photo\" --es path \""
+ remoteFilePath + "\" --ez usb " + usb + " --ei cameraId " + Integer.toString(cameraId) + " --ei channel "
+ Integer.toString(channel) + " --ei preset " + Integer.toString(preset) + " --es leftTopOsd \"" + leftTopOsd + "\"";
// adbShellResponse = null;
public boolean applyForPermissions() {
takePhotoImpl(cmd, channel, preset, fileName, remoteFilePath, photoOrVideo, 0);
String[] PM_MULTIPLE = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CHANGE_NETWORK_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_FINE_LOCATION
};
return 0;
}
try {
ArrayList<String> pmList = new ArrayList<>();
//获取当前未授权的权限列表
for(String permission:PM_MULTIPLE) {
int nRet = ContextCompat.checkSelfPermission(this,permission);
if(nRet != PackageManager.PERMISSION_GRANTED) {
pmList.add(permission);
}
}
protected long takeVideo(int channel, int preset, final boolean photoOrVideo) {
if (mAdb == null) {
return 0;
if (pmList.size() > 0) {
// Log.i(TAG,"进行权限申请...");
String[] sList = pmList.toArray(new String[0]);
ActivityCompat.requestPermissions(this, sList,REQUEST_CODE_PERMISSIONS);
return false;
}
} catch(Exception e){
e.printStackTrace();
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
Date dt = new Date();
int cameraId = channel - 1;
int duration = 5;
String leftTopOsd = "";
if (mAppConfig != null) {
leftTopOsd += mAppConfig.cmdid + " ";
}
SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
leftTopOsd += simpleDateFormat2.format(dt) + " ";
leftTopOsd += "CH:" + Integer.toString(channel) + " ";
final String fileName = "IMG-" + Integer.toString(channel) + "-" + simpleDateFormat.format(dt) + (photoOrVideo ? ".jpg" : ".mp4");
final String remoteFilePath = REMOTE_PATH_TMP + fileName;
// int quality = intent.getIntExtra("quality", 0);
// int width = intent.getIntExtra("width", 1280);
// int height = intent.getIntExtra("height", 720);
// int duration = intent.getIntExtra("duration", 15);
// int orientation = intent.getIntExtra("orientation", 0);
// long videoId = System.currentTimeMillis() / 1000;
//
// String leftTopOsd = intent.getStringExtra("leftTopOsd");
// String rightTopOsd = intent.getStringExtra("rightTopOsd");
// String rightBottomOsd = intent.getStringExtra("rightBottomOsd");
// String leftBottomOsd = intent.getStringExtra("leftBottomOsd");
String usb = (channel == 4) ? "true" : "false";
final String cmd = "am start -n " + PACKAGE_NAME_MP + "/" + PACKAGE_NAME_MP + ".BridgeActivity --es action \"recording\" --es path \""
+ remoteFilePath + "\" --ez usb " + usb + " --ei cameraId " + Integer.toString(cameraId) + " --ei channel "
+ Integer.toString(channel) + " --ei preset " + Integer.toString(preset) + " --ei duration " + Integer.toString(duration)
+ " --el videoId " + Long.toString(dt.getTime() / 1000) + " --es leftTopOsd \"" + leftTopOsd + "\"";
// adbShellResponse = null;
Log.d(TAG, cmd);
takePhotoImpl(cmd, channel, preset, fileName, remoteFilePath, photoOrVideo, duration * 1000);
return 0;
return true;
}
private void showPhoto(final String filePath) {
Intent intent = new Intent(MainActivity.this, ImageActivity.class);
intent.putExtra("path", filePath);
// intent.putExtra("info", info);
startActivity(intent);
}
protected void loadSettings() {
SharedPreferences preferences = getSharedPreferences("mpremote", MODE_PRIVATE);
private void showVideo(final String filePath) {
Intent intent = new Intent(MainActivity.this, VideoActivity.class);
intent.putExtra("path", filePath);
// intent.putExtra("info", info);
startActivity(intent);
mPassword = preferences.getString("password", DEFAULT_PRE_SHARED_KEY);
}
private void showAppInfo() {
ActionBar actionBar = getSupportActionBar();
// 视频浏览,拍照,设备信息及状态查询(编号版本双SIM卡状态电池及太阳能电压等),可以参考东视
Resources resources = getResources();
// String buildTime = BuildConfig.BUILD_
// Date date = new Date(BuildConfig.BUILD_TIMESTAMP);
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String title = resources.getString(R.string.app_name);
if (!TextUtils.isEmpty(mAppConfig.cmdid)) {
title += " - " + mAppConfig.cmdid;
}
actionBar.setTitle(title);
StringBuilder stringBuilder = new StringBuilder();
if (mProps.containsKey(KEY_RO_SERIALNO)) {
String val = (String)mProps.get(KEY_RO_SERIALNO);
appendKeyValue(stringBuilder, "序列号", val);
}
protected void openDeviceActivity() {
String ipAddressByWifi = null;
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (wifiManager != null) {
ipAddressByWifi = Formatter.formatIpAddress(wifiManager.getDhcpInfo().ipAddress);
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
appendKeyValue(stringBuilder, "CMDID", mAppConfig.cmdid);
if (ipAddressByWifi.contains(WIFI_IP_PREFIX)) {
appendKeyValue(stringBuilder, "主站", mAppConfig.server + ":" + Integer.toString(mAppConfig.port));
String deviceIP = Formatter.formatIpAddress(wifiManager.getDhcpInfo().gateway);
if (DEBUG) {
deviceIP = WIFI_IP_DEVICE;
}
appendKeyValue(stringBuilder, "通道数", Integer.toString(mAppConfig.channels));
Intent intent = new Intent(MainActivity.this, DeviceActivity.class);
// intent1.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("deviceIp", deviceIP);
intent.putExtra("SSID", mCurrentScanResult.SSID);
startActivity(intent);
}
if (mProps.containsKey(KEY_APP_MP_VERSION)) {
String val = (String)mProps.get(KEY_APP_MP_VERSION);
appendKeyValue(stringBuilder, "APP版本", val);
}
}
if (mBatteryVoltage != -1) {
appendKeyValue(stringBuilder, "电池电压", Integer.toString(mBatteryVoltage / 1000) + "." + Integer.toString((mBatteryVoltage % 1000) / 100));
private String normalizeSSID(String ssid) {
if (TextUtils.isEmpty(ssid)) {
return ssid;
}
if (mBatteryChargingVoltage != -1) {
appendKeyValue(stringBuilder, "充电电压", Integer.toString(mBatteryChargingVoltage / 1000) + "." + Integer.toString((mBatteryChargingVoltage % 1000) / 100));
if (!(ssid.startsWith("\"") && ssid.endsWith("\""))) {
return "\"" + ssid + "\"";
}
binding.deviceInfo.setText(Html.fromHtml(stringBuilder.toString()));
}
private void appendKeyValue(StringBuilder stringBuilder, String key, String val) {
stringBuilder.append(key + "<font color=\"red\"><b>" + val + "</b></font>");
stringBuilder.append("<br>");
return ssid;
}
/**

@ -0,0 +1,59 @@
package com.xypower.mpremote;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.MenuItem;
import com.xypower.mpremote.databinding.ActivitySettingsBinding;
public class SettingsActivity extends AppCompatActivity {
public static final int REQUEST_CODE_SETTINGS = 13;
public static final String DEFAULT_PRE_SHARED_KEY = "12345678";
private ActivitySettingsBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
SharedPreferences preferences = getSharedPreferences("mpremote", MODE_PRIVATE);
String pwd = preferences.getString("password", null);
if (pwd != null) {
binding.editViewPassword.setText(pwd);
}
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
@Override
protected void onDestroy() {
super.onDestroy();
SharedPreferences preferences = getSharedPreferences("mpremote", MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("password", binding.editViewPassword.getText().toString());
editor.commit();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
setResult(0);
finish();
return false;
default:
return super.onOptionsItemSelected(item);
}
}
}

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#0000FF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M1,9l2,2c4.97,-4.97 13.03,-4.97 18,0l2,-2C16.93,2.93 7.08,2.93 1,9zM9,17l3,3 3,-3c-1.65,-1.66 -4.34,-1.66 -6,0zM5,13l2,2c2.76,-2.76 7.24,-2.76 10,0l2,-2C15.14,9.14 8.87,9.14 5,13z"/>
</vector>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#0000FF">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4c3.42,0,6.73,1.27,9.3,3.53L12,18.85L2.7,7.53C5.27,5.27,8.58,4,12,4 M12,2C7.25,2,2.97,4.08,0,7.39L12,22L24,7.39 C21.03,4.08,16.75,2,12,2L12,2z" />
</vector>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#0000FF">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2.01c-4.75,0-9.03,2.08-12,5.39L12,22L24,7.4C21.03,4.09,16.75,2.01,12,2.01z M12,18.86L2.7,7.54 C5.27,5.28,8.58,4.01,12,4.01s6.73,1.27,9.3,3.53L12,18.86z" />
<path
android:fillColor="@android:color/white"
android:pathData="M16.42,16.63L12,22l-4.42-5.37c0.15-0.74,0.48-1.43,0.95-1.99C9.35,13.64,10.6,13,12,13s2.65,0.64,3.47,1.64 C15.94,15.2,16.27,15.89,16.42,16.63z" />
</vector>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#0000FF">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C7.25,2,2.97,4.08,0,7.39L12,22L24,7.39C21.03,4.08,16.75,2,12,2z M12,18.85L2.7,7.53C5.27,5.27,8.58,4,12,4 s6.73,1.27,9.3,3.53L12,18.85z" />
<path
android:fillColor="@android:color/white"
android:pathData="M18.61,13.95L12,22l-6.61-8.05c0.33-0.61,0.74-1.17,1.22-1.66c1.36-1.42,3.27-2.3,5.39-2.3s4.03,0.88,5.39,2.3 C17.87,12.78,18.28,13.34,18.61,13.95z" />
</vector>

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#0000FF">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C7.25,2,2.97,4.08,0,7.39L12,22L24,7.39C21.03,4.08,16.75,2,12,2z M12,18.85L2.7,7.53C5.27,5.27,8.58,4,12,4 s6.73,1.27,9.3,3.53L12,18.85z" />
<path
android:fillColor="@android:color/white"
android:pathData="M20.7,11.41L12,22L3.3,11.41c0.38-0.52,0.8-0.99,1.26-1.43C6.49,8.13,9.1,6.99,12,6.99s5.51,1.13,7.44,2.99 C19.9,10.41,20.33,10.89,20.7,11.41z" />
</vector>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#0000FF">
<path
android:fillColor="@android:color/white"
android:pathData="M24,7.39L12,22L0,7.39C2.97,4.08,7.25,2,12,2S21.03,4.08,24,7.39z" />
</vector>

@ -7,36 +7,24 @@
tools:context=".MainActivity">
<TextView
android:id="@+id/deviceInfo"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:singleLine="false"
android:lineSpacingMultiplier="1.25"
android:textSize="16sp"
android:text=""
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/takePhoto1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:text="通道1"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="请先连接到设备的热点"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/takeVideo1" />
app:layout_constraintTop_toTopOf="parent"
/>
<Button
android:id="@+id/takeVideo1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="24dp"
android:text="视频1"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
app:layout_constraintBottom_toBottomOf="parent" />

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".SettingsActivity">
<TextView
android:id="@+id/textViewPwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="32dp"
android:text="密码:"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editViewPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:ems="10"
android:inputType="textVisiblePassword"
android:text=""
app:layout_constraintStart_toEndOf="@id/textViewPwd"
app:layout_constraintTop_toTopOf="@id/textViewPwd"
app:layout_constraintBottom_toBottomOf="@id/textViewPwd" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/id_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_wifi" />
<TextView
android:id="@+id/id_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="2dp"
android:text="" />
</LinearLayout>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_search"
<item android:id="@+id/action_refresh"
android:icon="@drawable/ic_action_refresh"
android:title="@string/action_refresh"
app:showAsAction="always" />

@ -2,7 +2,10 @@
<string name="app_name">欣影微拍遥控</string>
<string name="err_dev_not_found">未发现微拍设备</string>
<string name="err_dev_failed_to_connect">连接设备失败</string>
<string name="title_activity_image"></string>
<string name="title_activity_image">照片</string>
<string name="title_activity_settings">设置</string>
<string name="title_activity_video">视频</string>
<string name="title_activity_device">微拍设备</string>
<string name="action_refresh">刷新</string>
<string name="action_settings">设置</string>
</resources>
Loading…
Cancel
Save