Skip to content

Instantly share code, notes, and snippets.

@jackyshan
Created July 17, 2018 01:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jackyshan/c5dea6986bf77ac764191db88ca6ec66 to your computer and use it in GitHub Desktop.
Save jackyshan/c5dea6986bf77ac764191db88ca6ec66 to your computer and use it in GitHub Desktop.
Android寻找行李
package com.baitrading.bes.ui;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import com.baitrading.bes.AppConfig;
import com.baitrading.bes.Constant;
import com.baitrading.bes.R;
import com.baitrading.bes.SyncUtil;
import com.baitrading.bes.common.LLog;
import com.baitrading.bes.common.OttoBus;
import com.baitrading.bes.common.events.BleOptEvent;
import com.baitrading.bes.common.events.RssiUpdateEvent;
import com.baitrading.bes.entity.AntiLostSettings;
import com.baitrading.bes.ui.view.InfoDialog;
import com.baitrading.bes.ui.view.TitleBar;
import com.baitrading.bes.util.Prefs;
import com.qmuiteam.qmui.widget.dialog.QMUIDialog;
import com.squareup.otto.Subscribe;
import com.suke.widget.SwitchButton;
import org.angmarch.views.NiceSpinner;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @author Administrator
*/
public class LuggageGuardActivity extends BaseActivity {
public static final String TAG = LuggageGuardActivity.class.getSimpleName();
private static final String DOT = ".";
@BindView(R.id.distance_state_img)
ImageView distanceStateImg;
@BindView(R.id.finding_state_txt)
TextView findingStateTxt;
@BindView(R.id.anti_lost_level_spinner)
NiceSpinner antiLostLevelSpinner;
@BindView(R.id.alarm_mode_spinner)
NiceSpinner alarmModeSpinner;
@BindView(R.id.help_center_view)
View helpCenterView;
@BindView(R.id.distance_txt)
TextView distanceTxt;
@BindView(R.id.anti_lost_txt)
TextView antiLostTxt;
@BindView(R.id.title_bar)
TitleBar titleBar;
@BindView(R.id.search_switch)
SwitchButton mSearchSwitch;
@BindView(R.id.luggage_guard_switch)
SwitchButton mLuggageGuardSwitch;
private Animation operatingAnim;
/**
* 最后一次读取到的 rssi ,用以比较来发现远近的变化
*/
private int lastRSSI;
/**
* 根据 rssi 查找设备,变远还是走近
*/
private boolean isSearching = false;
/**
* 防丢失使能状态,根据 rssi 算出距离
*/
private boolean antiLostEnable = false;
/**
* 正在读取 rssi ,查找设备 或 防丢失,其中一个功能开启了,则这个是正在读取
*/
private boolean readingRssi;
private Timer mReadRssiTimer;
private int antiLostLevel;
private int alarmType;
private InfoDialog alertDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_luggage_guard);
ButterKnife.bind(this);
OttoBus.getInstance().register(this);
titleBar.setOnTitleBarClickListener(new TitleBar.OnTitleBarClickListener() {
@Override
public void onLeftClick() {
onBackPressed();
}
@Override
public void onTitleClick() {
}
@Override
public void onRightClick() {
}
});
operatingAnim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.alpha_distance_scan);
operatingAnim.setInterpolator(new LinearInterpolator());
final List<String> levelSet = new LinkedList<>(Arrays.asList("Low", "Normal", "High"));
antiLostLevelSpinner.attachDataSource(levelSet);
antiLostLevelSpinner.addOnItemClickListener((parent, view, position, id) -> {
// 调节防丢失的灵敏度
AntiLostSettings.setAntiLostLevel(getApplicationContext(), position);
});
final List<String> modeSet = new LinkedList<>(Arrays.asList(getString(R.string.vibrate_only), getString(R.string.alarm_only), getString(R.string.vibrate_and_alarm)));
alarmModeSpinner.attachDataSource(modeSet);
alarmModeSpinner.addOnItemClickListener((parent, view, position, id) -> {
AntiLostSettings.setAntiLostAlarmType(getApplicationContext(), position);
});
mSearchSwitch.setOnCheckedChangeListener((view, isChecked) -> {
// 点击按钮切换搜索状态
isSearching = isChecked;
if (isSearching) {
// 开启搜索功能,如果当前没有在读取 rssi ,那么开启之
if (!readingRssi) {
startReadRssi();
}
} else {
// 关闭搜索设备功能,如果此时没有开启防丢失,那么就不再需要读取 rssi,关闭之
if (!antiLostEnable) {
stopReadRssi();
}
}
});
mLuggageGuardSwitch.setOnCheckedChangeListener((view, isChecked) -> {
antiLostEnable = isChecked;
Prefs.with(getApplicationContext()).writeBoolean(Constant.KEY_ANTI_LOST_ALARM_ENABLE, antiLostEnable);
if (antiLostEnable) {
// 开启防丢失功能,如果此时没有在读取 rssi ,则开启之
Log.d(TAG, "允许了 anti lost");
if (!readingRssi) {
startReadRssi();
}
} else {
// 关闭防丢失功能,如果当前是没有在搜索设备,那么关闭之
Log.d(TAG, "禁止了 anti lost");
if (!isSearching) {
readRssiCount = 0;
rssiTotal = 0;
stopReadRssi();
}
}
});
}
@Override
protected void onResume() {
super.onResume();
// 是否已经开启了防丢失
antiLostEnable = Prefs.with(getApplicationContext()).readBoolean(Constant.KEY_ANTI_LOST_ALARM_ENABLE, false);
mLuggageGuardSwitch.setChecked(antiLostEnable);
antiLostLevel = AntiLostSettings.getAntiLostLevel(getApplicationContext());
antiLostLevelSpinner.setSelectedIndex(antiLostLevel);
alarmType = AntiLostSettings.getAntiLostAlarmType(getApplicationContext());
alarmModeSpinner.setSelectedIndex(alarmType);
mSearchSwitch.setChecked(isSearching);
// 两个功能中, 有一个开启了,那就开启读取 rssi
if (isSearching || antiLostEnable) {
startReadRssi();
}
distanceNormal();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
stopReadRssi();
OttoBus.getInstance().unregister(this);
}
private void startReadRssi() {
if (AppConfig.isDebug) {
toast("startReadRssi");
}
// 开始读取 rssi ,发送指令让设备不再休眠
SyncUtil.currentNoSleep = true;
OttoBus.getInstance().post(new BleOptEvent(BleOptEvent.SEND_COMMAND));
readingRssi = true;
if (mReadRssiTimer != null) {
mReadRssiTimer.cancel();
}
mReadRssiTimer = new Timer();
mReadRssiTimer.schedule(new ReadRssiTask(), 1000, 100);
}
private void stopReadRssi() {
if (AppConfig.isDebug) {
toast("stopReadRssi");
}
// 可以休眠了
readRssiCount = 0;
rssiTotal = 0;
SyncUtil.currentNoSleep = false;
OttoBus.getInstance().post(new BleOptEvent(BleOptEvent.SEND_COMMAND));
readingRssi = false;
if (mReadRssiTimer != null) {
mReadRssiTimer.cancel();
}
distanceNormal();
}
public class ReadRssiTask extends TimerTask {
@Override
public void run() {
runOnUiThread(() -> {
OttoBus.getInstance().post(new BleOptEvent(BleOptEvent.READ_RSSI));
});
}
}
@Subscribe
public void onRssiUpdateListener(RssiUpdateEvent event) {
rssiUpdate(event.getRssi());
}
/**
* 当前读取了多少次 rssi
*/
private int readRssiCount = 0;
/**
* 读取周期内的 rssi 总和
*/
private int rssiTotal = 0;
/**
* 读取几次为一个统计周期
*/
private int readPeriod = 15;
public void rssiUpdate(int rssi) {
// 累加
readRssiCount += 1;
rssiTotal += rssi;
if (readRssiCount % readPeriod != 0) {
// 没到一个周期,不处理
return;
} else {
// 累计够了次数,则计算平均 rssi ,并清 0 rssiTotal
rssi = rssiTotal / readPeriod;
rssiTotal = 0;
// 记录第一次统计的 RSSI,设置为 A 值,用于计算距离
if (readRssiCount < 19) {
SyncUtil.RSSI_A = Math.abs(rssi);
LLog.e("RSSI_A=", SyncUtil.RSSI_A + "");
}
}
// 计算距离
double distance = getDistance(rssi);
// 格式化打印日志
DecimalFormat df = new DecimalFormat("#.00");
String d = df.format(distance);
if (d.startsWith(DOT)) {
d = "0" + d;
}
LLog.e(TAG, "rssi:" + rssi + " ; distance: " + d + "m");
// 如果开启了距离搜索
if (isSearching) {
// 两次变化的 rssi 超过 5 才会有比较的意义
if (Math.abs(Math.abs(rssi) - Math.abs(lastRSSI)) > 5) {
if (Math.abs(rssi) > Math.abs(lastRSSI)) {
distanceFarther();
} else {
distanceCloser();
}
// 将上一次有效的比较值,作为上一次的值,用于下次比较
lastRSSI = rssi;
}
}
// 开启了防丢失
if (antiLostEnable) {
// 判断距离,作出告警
int antiLostLevel = AntiLostSettings.getAntiLostLevel(getApplicationContext());
int antiLostDistance = AntiLostSettings.getDistanceByLevel(antiLostLevel);
if (distance > antiLostDistance) {
LLog.e(TAG, "防丢失告警");
if (alertDialog != null && alertDialog.isShowing()) {
Log.d(TAG, "正在 alarm,不再显示第二次");
return;
}
toast("Luggage Guard");
showNotification(getApplicationContext(), "Luggage Guard", "Check your Luggage");
if (alarmModeSpinner.getSelectedIndex() == 0) {
vibrate(4);
} else if (alarmModeSpinner.getSelectedIndex() == 1) {
ringStart();
SyncUtil.currentFmqLongBuzzer = true;
} else {
vibrate(4);
ringStart();
SyncUtil.currentFmqLongBuzzer = true;
}
// 长鸣告警
OttoBus.getInstance().post(new BleOptEvent(BleOptEvent.SEND_COMMAND));
SyncUtil.currentFmqLongBuzzer = false;
alertDialog = new InfoDialog(this);
alertDialog.show();
alertDialog.setTitleTxt("ALERT");
alertDialog.setMessageTxt("Check Your Luggage");
alertDialog.setInfoImg(R.drawable.ic_dialog_lost);
alertDialog.setCenterClickListener("Close", v -> {
alertDialog.dismiss();
if (alarmModeSpinner.getSelectedIndex() == 0) {
vibrateStop();
} else if (alarmModeSpinner.getSelectedIndex() == 1) {
ringStop();
} else {
vibrateStop();
ringStop();
}
SyncUtil.currentFmqLongBuzzer = false;
OttoBus.getInstance().post(new BleOptEvent(BleOptEvent.SEND_COMMAND));
mLuggageGuardSwitch.setChecked(false);
});
}
}
}
/**
* RSSI 转成距离:米
*
* @param rssi
* @return 距离/米
*/
private double getDistance(int rssi) {
double distance = 0;
double absRSSI = Math.abs(rssi);
double a = SyncUtil.RSSI_A;
double n = SyncUtil.RSSI_n;
double pow = (absRSSI - a) / (10 * n);
distance = Math.pow(10, pow);
return distance;
}
private void distanceNormal() {
distanceStateImg.setVisibility(View.INVISIBLE);
findingStateTxt.setText("");
}
private void distanceCloser() {
distanceStateImg.setVisibility(View.VISIBLE);
distanceStateImg.setImageResource(R.drawable.ic_closer);
distanceStateImg.setAnimation(operatingAnim);
operatingAnim.start();
findingStateTxt.setText("");
}
private void distanceFarther() {
distanceStateImg.setVisibility(View.VISIBLE);
distanceStateImg.setImageResource(R.drawable.ic_farther);
distanceStateImg.setAnimation(operatingAnim);
operatingAnim.start();
findingStateTxt.setText("");
}
public void changeA(View view) {
final QMUIDialog.EditTextDialogBuilder builder = new QMUIDialog.EditTextDialogBuilder(this);
builder.setTitle("当前 A 值")
.setPlaceholder("A")
.setInputType(InputType.TYPE_CLASS_NUMBER)
.addAction("cancel", (dialog, index) -> dialog.dismiss())
.addAction("ok", (dialog, index) -> {
CharSequence text = builder.getEditText().getText();
if (text != null && text.length() > 0) {
try {
float a = Float.parseFloat(text.toString());
SyncUtil.RSSI_A = a;
dialog.dismiss();
} catch (NumberFormatException e) {
toast("must input number");
}
} else {
toast("year can't empty");
}
})
.show();
builder.getEditText().setText(SyncUtil.RSSI_A + "");
}
public void changeN(View view) {
final QMUIDialog.EditTextDialogBuilder builder = new QMUIDialog.EditTextDialogBuilder(this);
builder.setTitle("当前 n 值")
.setPlaceholder("n")
.setInputType(InputType.TYPE_CLASS_NUMBER)
.addAction("cancel", (dialog, index) -> dialog.dismiss())
.addAction("ok", (dialog, index) -> {
CharSequence text = builder.getEditText().getText();
if (text != null && text.length() > 0) {
try {
float n = Float.parseFloat(text.toString());
SyncUtil.RSSI_n = n;
dialog.dismiss();
} catch (NumberFormatException e) {
toast("must input number");
}
} else {
toast("year can't empty");
}
})
.show();
builder.getEditText().setText(SyncUtil.RSSI_n + "");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment