a few optimizations

feat: floating window permission is optinal

feat: jump to the last position when starting

feat: welcome activity is only displayed at the first time

feat: add commit hash to version

fix: the HTTP request cannot retrieve the address information
This commit is contained in:
EricTeo 2024-12-22 16:45:42 +08:00
parent 69047069af
commit 964358d264
9 changed files with 125 additions and 119 deletions

View File

@ -1,5 +1,11 @@
apply plugin: 'com.android.application'
def getGitCommitHash() {
return providers.exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
}.standardOutput.asText.get().trim()
}
android {
signingConfigs {
debug {
@ -24,7 +30,7 @@ android {
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 32
versionCode 1121
versionName '1.12.1' // https://semver.org/lang/zh-CN/
versionName '1.12.1-' + getGitCommitHash()
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs 'zh', 'zh-rCN', 'en', 'en-rUS'
ndk {

View File

@ -109,7 +109,6 @@ import io.noties.markwon.Markwon;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class MainActivity extends BaseActivity implements SensorEventListener {
@ -137,7 +136,6 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
private MapView mMapView;
private static BaiduMap mBaiduMap = null;
private static LatLng mMarkLatLngMap = new LatLng(36.547743718042415, 117.07018449827267); // 当前标记的地图点
private static String mMarkName = null;
private GeoCoder mGeoCoder;
private SensorManager mSensorManager;
private Sensor mSensorAccelerometer;
@ -158,6 +156,8 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
private FloatingActionButton mButtonStart;
/*============================== 历史记录 相关 ==============================*/
private SQLiteDatabase mLocationHistoryDB;
private GeoCoder mLocationHistoryGeoCoder;
private ContentValues mLocationHistoryValues;
private SQLiteDatabase mSearchHistoryDB;
/*============================== SearchView 相关 ==============================*/
private SearchView searchView;
@ -220,7 +220,22 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
initUpdateVersion();
checkUpdateVersion(false);
try {
Cursor cursor = mLocationHistoryDB.query(DataBaseHistoryLocation.TABLE_NAME, null,
DataBaseHistoryLocation.DB_COLUMN_ID + " > ?", new String[]{"0"},
null, null, DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP + " DESC", null);
if (cursor.moveToFirst()) {
String name = cursor.getString(1);
String bd09Longitude = cursor.getString(5);
String bd09Latitude = cursor.getString(6);
MainActivity.showLocation(name, bd09Longitude, bd09Latitude);
isFirstLoc = false;
}
cursor.close();
} catch (Exception ignored) {
}
// 开始定位
mLocClient.start();
}
@Override
@ -434,8 +449,16 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
} catch (Exception e) {
GoUtils.DisplayToast(this, getResources().getString(R.string.app_error_dev));
}
} else if (id == R.id.nav_overlays) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
GoUtils.DisplayToast(this, "无法跳转到悬浮窗权限设置界面");
}
} else if (id == R.id.nav_update) {
checkUpdateVersion(true);
checkUpdateVersion();
} else if (id == R.id.nav_feedback) {
File file = new File(getExternalFilesDir("Logs"), GoApplication.LOG_FILE_NAME);
ShareUtils.shareFile(this, file, item.getTitle().toString());
@ -697,7 +720,6 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
if (reverseGeoCodeResult == null || reverseGeoCodeResult.error != SearchResult.ERRORNO.NO_ERROR) {
XLog.i("逆地理位置失败!");
} else {
mMarkName = String.valueOf(reverseGeoCodeResult.getAddress());
poiLatitude.setText(String.valueOf(reverseGeoCodeResult.getLocation().latitude));
poiLongitude.setText(String.valueOf(reverseGeoCodeResult.getLocation().longitude));
poiAddress.setText(reverseGeoCodeResult.getAddress());
@ -779,8 +801,6 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
LocationClientOption locationOption = getLocationClientOption();
//需将配置好的LocationClientOption对象通过setLocOption方法传递给LocationClient对象使用
mLocClient.setLocOption(locationOption);
//开始定位
mLocClient.start();
} catch (Exception e) {
XLog.e("ERROR: initMapLocation");
}
@ -875,8 +895,6 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
double[] bdLonLat = MapUtils.wgs2bd09(dialog_lat_double, dialog_lng_double);
mMarkLatLngMap = new LatLng(bdLonLat[1], bdLonLat[0]);
}
mMarkName = "手动输入的坐标";
markMap();
MapStatusUpdate mapstatusupdate = MapStatusUpdateFactory.newLatLng(mMarkLatLngMap);
@ -914,7 +932,7 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
mBaiduMap.setMyLocationData(locData);
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(new LatLng(mCurrentLat, mCurrentLon)).zoom(18.0f);
builder.target(new LatLng(mCurrentLat, mCurrentLon)).zoom(18.0f).rotate(0);
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
}
@ -992,13 +1010,15 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
try {
if (!bd09Longitude.isEmpty() && !bd09Latitude.isEmpty()) {
mMarkName = name;
mMarkLatLngMap = new LatLng(Double.parseDouble(bd09Latitude), Double.parseDouble(bd09Longitude));
MarkerOptions ooA = new MarkerOptions().position(mMarkLatLngMap).icon(mMapIndicator);
mBaiduMap.clear();
mBaiduMap.addOverlay(ooA);
MapStatusUpdate mapstatusupdate = MapStatusUpdateFactory.newLatLng(mMarkLatLngMap);
mBaiduMap.setMapStatus(mapstatusupdate);
MapStatus.Builder builder = new MapStatus.Builder();
builder.target(mMarkLatLngMap).zoom(18).rotate(0);
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
}
} catch (Exception e) {
ret = false;
@ -1046,33 +1066,19 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
return;
}
if (!Settings.canDrawOverlays(getApplicationContext())) {//悬浮窗权限判断
GoUtils.showEnableFloatWindowDialog(this);
XLog.e("无悬浮窗权限!");
return;
}
if (isMockServStart) {
if (mMarkLatLngMap == null) {
stopGoLocation();
Snackbar.make(v, "模拟位置已终止", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
Snackbar.make(v, "模拟位置已终止", Snackbar.LENGTH_LONG).show();
mButtonStart.setImageResource(R.drawable.ic_position);
} else {
double[] latLng = MapUtils.bd2wgs(mMarkLatLngMap.longitude, mMarkLatLngMap.latitude);
double alt = Double.parseDouble(sharedPreferences.getString("setting_altitude", "55.0"));
mServiceBinder.setPosition(latLng[0], latLng[1], alt);
Snackbar.make(v, "已传送到新位置", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
recordCurrentLocation(mMarkLatLngMap.longitude, mMarkLatLngMap.latitude);
mBaiduMap.clear();
mMarkLatLngMap = null;
if (GoUtils.isWifiEnabled(MainActivity.this)) {
GoUtils.showDisableWifiDialog(MainActivity.this);
}
GoUtils.showLocationNotice(this, v, true);
}
} else {
if (!GoUtils.isAllowMockLocation(this)) {
@ -1080,21 +1086,14 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
XLog.e("无模拟位置权限!");
} else {
if (mMarkLatLngMap == null) {
Snackbar.make(v, "请先点击地图位置或者搜索位置", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
Snackbar.make(v, "请先点击地图位置或者搜索位置", Snackbar.LENGTH_LONG).show();
} else {
startGoLocation();
mButtonStart.setImageResource(R.drawable.ic_fly);
Snackbar.make(v, "模拟位置已启动", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
recordCurrentLocation(mMarkLatLngMap.longitude, mMarkLatLngMap.latitude);
mBaiduMap.clear();
mMarkLatLngMap = null;
if (GoUtils.isWifiEnabled(MainActivity.this)) {
GoUtils.showDisableWifiDialog(MainActivity.this);
}
GoUtils.showLocationNotice(this, v, false);
}
}
}
@ -1112,6 +1111,21 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
} catch (Exception e) {
XLog.e("ERROR: sqlite init error");
}
mLocationHistoryValues = new ContentValues();
mLocationHistoryGeoCoder = GeoCoder.newInstance();
mLocationHistoryGeoCoder.setOnGetGeoCodeResultListener(new OnGetGeoCoderResultListener() {
@Override
public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
}
@Override
public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
if (reverseGeoCodeResult != null && reverseGeoCodeResult.error == SearchResult.ERRORNO.NO_ERROR) {
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, reverseGeoCodeResult.getSematicDescription());
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, mLocationHistoryValues);
}
}
});
}
//获取查询历史
@ -1143,80 +1157,16 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
// 记录请求的位置信息
private void recordCurrentLocation(double lng, double lat) {
//参数坐标系bd09
final String safeCode = getResources().getString(R.string.safecode);
final String ak = getResources().getString(R.string.ak);
// 参数坐标系bd09
double[] latLng = MapUtils.bd2wgs(lng, lat);
//bd09坐标的位置信息
String mapApiUrl = "https://api.map.baidu.com/reverse_geocoding/v3/?ak=" + ak + "&output=json&coordtype=bd09ll" + "&location=" + lat + "," + lng + "&mcode=" + safeCode;
okhttp3.Request request = new okhttp3.Request.Builder().url(mapApiUrl).get().build();
final Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
//http 请求失败
XLog.e("HTTP: HTTP GET FAILED");
//插表参数
ContentValues contentValues = new ContentValues();
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, mMarkName);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_WGS84, String.valueOf(latLng[0]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_WGS84, String.valueOf(latLng[1]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP, System.currentTimeMillis() / 1000);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_CUSTOM, Double.toString(lng));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_CUSTOM, Double.toString(lat));
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, contentValues);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
ResponseBody responseBody = response.body();
if (responseBody != null) {
String resp = responseBody.string();
try {
JSONObject getRetJson = new JSONObject(resp);
//位置获取成功
if (Integer.parseInt(getRetJson.getString("status")) == 0) {
JSONObject posInfoJson = getRetJson.getJSONObject("result");
String formatted_address = posInfoJson.getString("formatted_address");
//插表参数
ContentValues contentValues = new ContentValues();
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, formatted_address);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_WGS84, String.valueOf(latLng[0]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_WGS84, String.valueOf(latLng[1]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP, System.currentTimeMillis() / 1000);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_CUSTOM, Double.toString(lng));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_CUSTOM, Double.toString(lat));
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, contentValues);
} else {
ContentValues contentValues = new ContentValues();
// contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, getResources().getString(R.string.history_location_default_name));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, mMarkName);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_WGS84, String.valueOf(latLng[0]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_WGS84, String.valueOf(latLng[1]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP, System.currentTimeMillis() / 1000);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_CUSTOM, Double.toString(lng));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_CUSTOM, Double.toString(lat));
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, contentValues);
}
} catch (JSONException e) {
XLog.e("JSON: resolve json error");
//插表参数
ContentValues contentValues = new ContentValues();
// contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, getResources().getString(R.string.history_location_default_name));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, mMarkName);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_WGS84, String.valueOf(latLng[0]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_WGS84, String.valueOf(latLng[1]));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP, System.currentTimeMillis() / 1000);
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_CUSTOM, Double.toString(lng));
contentValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_CUSTOM, Double.toString(lat));
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, contentValues);
}
}
}
});
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LOCATION, "");
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_WGS84, String.valueOf(latLng[0]));
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_WGS84, String.valueOf(latLng[1]));
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_TIMESTAMP, System.currentTimeMillis() / 1000);
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LONGITUDE_CUSTOM, Double.toString(lng));
mLocationHistoryValues.put(DataBaseHistoryLocation.DB_COLUMN_LATITUDE_CUSTOM, Double.toString(lat));
DataBaseHistoryLocation.saveHistoryLocation(mLocationHistoryDB, mLocationHistoryValues);
mLocationHistoryGeoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(new LatLng(lat, lng)));
}
/*============================== SearchView 相关 ==============================*/
@ -1228,7 +1178,7 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
mSearchList.setOnItemClickListener((parent, view, position, id) -> {
String lng = ((TextView) view.findViewById(R.id.poi_longitude)).getText().toString();
String lat = ((TextView) view.findViewById(R.id.poi_latitude)).getText().toString();
mMarkName = ((TextView) view.findViewById(R.id.poi_name)).getText().toString();
String markName = ((TextView) view.findViewById(R.id.poi_name)).getText().toString();
mMarkLatLngMap = new LatLng(Double.parseDouble(lat), Double.parseDouble(lng));
MapStatusUpdate mapstatusupdate = MapStatusUpdateFactory.newLatLng(mMarkLatLngMap);
mBaiduMap.setMapStatus(mapstatusupdate);
@ -1241,7 +1191,7 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
// mSearchList.setVisibility(View.GONE);
//搜索历史 插表参数
ContentValues contentValues = new ContentValues();
contentValues.put(DataBaseHistorySearch.DB_COLUMN_KEY, mMarkName);
contentValues.put(DataBaseHistorySearch.DB_COLUMN_KEY, markName);
contentValues.put(DataBaseHistorySearch.DB_COLUMN_DESCRIPTION, ((TextView) view.findViewById(R.id.poi_address)).getText().toString());
contentValues.put(DataBaseHistorySearch.DB_COLUMN_IS_LOCATION, DataBaseHistorySearch.DB_SEARCH_TYPE_RESULT);
contentValues.put(DataBaseHistorySearch.DB_COLUMN_LONGITUDE_CUSTOM, lng);
@ -1265,7 +1215,6 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
if (searchIsLoc.equals("1")) {
String lng = ((TextView) view.findViewById(R.id.search_longitude)).getText().toString();
String lat = ((TextView) view.findViewById(R.id.search_latitude)).getText().toString();
// mMarkName = ((TextView) view.findViewById(R.id.poi_name)).getText().toString();
mMarkLatLngMap = new LatLng(Double.parseDouble(lat), Double.parseDouble(lng));
MapStatusUpdate mapstatusupdate = MapStatusUpdateFactory.newLatLng(mMarkLatLngMap);
mBaiduMap.setMapStatus(mapstatusupdate);
@ -1395,15 +1344,24 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
registerReceiver(mDownloadBdRcv, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
private void checkUpdateVersion(boolean result) {
private void checkUpdateVersion() {
String mapApiUrl = "https://api.github.com/repos/zcshou/gogogo/releases/latest";
okhttp3.Request request = new okhttp3.Request.Builder().url(mapApiUrl).get().build();
final Call call = mOkHttpClient.newCall(request);
call.enqueue(new Callback() {
private void showFail() {
View v = findViewById(android.R.id.content);
Snackbar.make(v, "获取更新信息失败", Snackbar.LENGTH_LONG).setAction("去浏览器看看", view -> {
Uri uri = Uri.parse("https://github.com/ZCShou/GoGoGo/releases");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}).show();
}
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
XLog.i("更新检测失败");
showFail();
}
@Override
@ -1458,12 +1416,11 @@ public class MainActivity extends BaseActivity implements SensorEventListener {
});
}
} else {
if (result) {
GoUtils.DisplayToast(MainActivity.this, getResources().getString(R.string.update_last));
}
GoUtils.DisplayToast(MainActivity.this, getResources().getString(R.string.update_last));
}
} catch (JSONException e) {
XLog.e("ERROR: resolve json");
showFail();
}
});
}

View File

@ -276,8 +276,7 @@ public class WelcomeActivity extends AppCompatActivity {
if (mPrivacy && mAgreement) {
checkBox.setChecked(true);
checkDefaultPermissions();
} else {
checkBox.setChecked(false);
startMainActivity();
}
}

View File

@ -6,6 +6,7 @@ import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.PixelFormat;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@ -159,6 +160,9 @@ public class JoyStick extends View {
}
public void show() {
if (!Settings.canDrawOverlays(mContext)) {
return;
}
switch (mCurWin) {
case WINDOW_TYPE_MAP:
if (mJoystickLayout.getParent() != null) {

View File

@ -192,6 +192,7 @@ public class ServiceGo extends Service {
sendEmptyMessage(HANDLER_MSG_ID);
}
mJoyStick.show();
} catch (InterruptedException e) {
XLog.e("SERVICEGO: ERROR - handleMessage");
Thread.currentThread().interrupt();

View File

@ -18,10 +18,13 @@ import android.os.Build;
import android.os.CountDownTimer;
import android.provider.Settings;
import android.view.Gravity;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.snackbar.Snackbar;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@ -246,6 +249,28 @@ public class GoUtils {
.show();
}
public static void showLocationNotice(Context context, View view, boolean started) {
String text = started ? "已传送到新位置" : "模拟位置已启动";
if (Settings.canDrawOverlays(context)) {
Snackbar.make(view, text, Snackbar.LENGTH_LONG).show();
} else {
text += ";为了模拟定位的稳定性,建议开启\"显示悬浮窗\"权限";
Snackbar.make(view, text, Snackbar.LENGTH_LONG).setAction("去设置", v -> {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + context.getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} catch (Exception e) {
DisplayToast(context, "无法跳转到悬浮窗权限设置界面");
}
}).show();
}
if (isWifiEnabled(context) && !started) {
Toast.makeText(context, "开启 WIFI (即使未连接) 可能导致虚拟定位失败", Toast.LENGTH_SHORT).show();
}
}
public static void DisplayToast(Context context, String str) {
Toast toast = Toast.makeText(context, str, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP, 0, 100);

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="580"
android:viewportHeight="400">
<path
android:pathData="m291.99997,400.34956l-290.94447,-146.50363l53.33982,-26.16136l237.60465,119.29581l237.60465,-119.29581l53.33982,26.16136l-290.94447,146.50363zm0,-105.6919l-290.94447,-146.50363l290.94447,-146.50363l290.94447,146.50363l-290.94447,146.50363zm0,-146.50363zm0,93.13445l185.88119,-93.13445l-185.88119,-93.13445l-185.88119,93.13445l185.88119,93.13445z"
android:fillColor="#FF000000"/>
</vector>

View File

@ -17,6 +17,10 @@
android:id="@+id/nav_settings"
android:icon="@drawable/ic_menu_settings"
android:title="@string/nav_menu_settings" />
<item
android:id="@+id/nav_overlays"
android:icon="@drawable/ic_menu_overlays"
android:title="@string/nav_menu_overlays" />
<item
android:id="@+id/nav_dev"
android:icon="@drawable/ic_menu_dev"

View File

@ -106,6 +106,7 @@
<string name="nav_menu_history">历史记录</string>
<string name="nav_menu_settings">设置</string>
<string name="nav_menu_dev">开发人员选项</string>
<string name="nav_menu_overlays">悬浮窗权限</string>
<string name="nav_menu_more">更多</string>
<string name="nav_menu_upgrade">检测更新</string>
<string name="nav_menu_feedback">问题反馈</string>