Merge branch 'V2.5'
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
OpenSeizureDetector Android App - Change Log
|
OpenSeizureDetector Android App - Change Log
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
|
V2.5.5 - 23feb2018 - Improved logging to help diagnose network problems
|
||||||
|
that result in 'net fault' warnings from network data source
|
||||||
|
('wifi-storms').
|
||||||
|
|
||||||
V2.5.4 - 03dec2017 - Added option to use either tone generator or MP3 alarm sound, as a user reported problem with tone generator on high end samsung phone.
|
V2.5.4 - 03dec2017 - Added option to use either tone generator or MP3 alarm sound, as a user reported problem with tone generator on high end samsung phone.
|
||||||
|
|
||||||
V2.5.3 - 10sep2017 - Added Pebble App V2.6 which provides better alarm reliability
|
V2.5.3 - 10sep2017 - Added Pebble App V2.6 which provides better alarm reliability
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":38},"path":"app-release.apk","properties":{"packageId":"uk.org.openseizuredetector","split":"","minSdkVersion":"14"}}]
|
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":41},"path":"app-release.apk","properties":{"packageId":"uk.org.openseizuredetector","split":"","minSdkVersion":"14"}}]
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="uk.org.openseizuredetector"
|
package="uk.org.openseizuredetector"
|
||||||
android:versionCode="38"
|
android:versionCode="41"
|
||||||
android:versionName="2.5.4">
|
android:versionName="2.5.5">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ public class SdDataSourceNetwork extends SdDataSource {
|
|||||||
private Time mStatusTime;
|
private Time mStatusTime;
|
||||||
private Timer mDataUpdateTimer;
|
private Timer mDataUpdateTimer;
|
||||||
private int mDataUpdatePeriod = 2000;
|
private int mDataUpdatePeriod = 2000;
|
||||||
|
private int mConnnectTimeoutPeriod = 5000;
|
||||||
|
private int mReadTimeoutPeriod = 5000;
|
||||||
private String mServerIP = "unknown";
|
private String mServerIP = "unknown";
|
||||||
|
|
||||||
private int ALARM_STATE_NETFAULT = 7;
|
private int ALARM_STATE_NETFAULT = 7;
|
||||||
@@ -92,6 +94,12 @@ public class SdDataSourceNetwork extends SdDataSource {
|
|||||||
String dataUpdatePeriodStr = SP.getString("DataUpdatePeriod","2000");
|
String dataUpdatePeriodStr = SP.getString("DataUpdatePeriod","2000");
|
||||||
mDataUpdatePeriod = Integer.parseInt(dataUpdatePeriodStr);
|
mDataUpdatePeriod = Integer.parseInt(dataUpdatePeriodStr);
|
||||||
Log.v(TAG,"updatePrefs() - mDataUpdatePeriod = "+mDataUpdatePeriod);
|
Log.v(TAG,"updatePrefs() - mDataUpdatePeriod = "+mDataUpdatePeriod);
|
||||||
|
String connectTimeoutPeriodStr = SP.getString("ConnectTimeoutPeriod","5000");
|
||||||
|
mConnnectTimeoutPeriod = Integer.parseInt(connectTimeoutPeriodStr);
|
||||||
|
Log.v(TAG,"updatePrefs() - mConnectTimeoutPeriod = "+mConnnectTimeoutPeriod);
|
||||||
|
String readTimeoutPeriodStr = SP.getString("ReadTimeoutPeriod","5000");
|
||||||
|
mReadTimeoutPeriod = Integer.parseInt(readTimeoutPeriodStr);
|
||||||
|
Log.v(TAG,"updatePrefs() - mReadTimeoutPeriod = "+mReadTimeoutPeriod);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.v(TAG,"updatePrefs() - Problem parsing preferences!");
|
Log.v(TAG,"updatePrefs() - Problem parsing preferences!");
|
||||||
mUtil.writeToSysLogFile("SdDataSourceNetwork().updatePrefs() - " +ex.toString());
|
mUtil.writeToSysLogFile("SdDataSourceNetwork().updatePrefs() - " +ex.toString());
|
||||||
@@ -156,9 +164,7 @@ public class SdDataSourceNetwork extends SdDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrive the current Seizure Detector Data from the server.
|
* Accept an alarm remotely using a http GET request.
|
||||||
* Uses the DownloadSdDataTask class to download the data in the
|
|
||||||
* background. The data is processed in DownloadSdDataTask.onPostExecute().
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void acceptAlarm() {
|
public void acceptAlarm() {
|
||||||
@@ -203,8 +209,8 @@ public class SdDataSourceNetwork extends SdDataSource {
|
|||||||
try {
|
try {
|
||||||
URL url = new URL(myurl);
|
URL url = new URL(myurl);
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
conn.setReadTimeout(5000 /* milliseconds */);
|
conn.setReadTimeout(mReadTimeoutPeriod /* milliseconds */);
|
||||||
conn.setConnectTimeout(5000 /* milliseconds */);
|
conn.setConnectTimeout(mConnnectTimeoutPeriod /* milliseconds */);
|
||||||
conn.setRequestMethod("GET");
|
conn.setRequestMethod("GET");
|
||||||
conn.setDoInput(true);
|
conn.setDoInput(true);
|
||||||
// Starts the query
|
// Starts the query
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ public class SdDataSourcePebble extends SdDataSource {
|
|||||||
*/
|
*/
|
||||||
public void updatePrefs() {
|
public void updatePrefs() {
|
||||||
Log.v(TAG, "updatePrefs()");
|
Log.v(TAG, "updatePrefs()");
|
||||||
mUtil.writeToSysLogFile("SdDataSourcePebble.updatePrefs()");
|
//mUtil.writeToSysLogFile("SdDataSourcePebble.updatePrefs()");
|
||||||
SharedPreferences SP = PreferenceManager
|
SharedPreferences SP = PreferenceManager
|
||||||
.getDefaultSharedPreferences(mContext);
|
.getDefaultSharedPreferences(mContext);
|
||||||
try {
|
try {
|
||||||
@@ -527,7 +527,7 @@ public class SdDataSourcePebble extends SdDataSource {
|
|||||||
*/
|
*/
|
||||||
public void getPebbleSdSettings() {
|
public void getPebbleSdSettings() {
|
||||||
Log.v(TAG, "getPebbleSdSettings() - sending required settings to pebble");
|
Log.v(TAG, "getPebbleSdSettings() - sending required settings to pebble");
|
||||||
mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings()");
|
//mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings()");
|
||||||
sendPebbleSdSettings();
|
sendPebbleSdSettings();
|
||||||
//Log.v(TAG, "getPebbleSdSettings() - requesting settings from pebble");
|
//Log.v(TAG, "getPebbleSdSettings() - requesting settings from pebble");
|
||||||
//mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings() - and request settings from pebble");
|
//mUtil.writeToSysLogFile("SdDataSourcePebble.getPebbleSdSettings() - and request settings from pebble");
|
||||||
@@ -545,7 +545,7 @@ public class SdDataSourcePebble extends SdDataSource {
|
|||||||
*/
|
*/
|
||||||
public void sendPebbleSdSettings() {
|
public void sendPebbleSdSettings() {
|
||||||
Log.v(TAG, "sendPebblSdSettings() - preparing settings dictionary.. mSampleFreq=" + mSampleFreq);
|
Log.v(TAG, "sendPebblSdSettings() - preparing settings dictionary.. mSampleFreq=" + mSampleFreq);
|
||||||
mUtil.writeToSysLogFile("SdDataSourcePebble.sendPebbleSdSettings()");
|
//mUtil.writeToSysLogFile("SdDataSourcePebble.sendPebbleSdSettings()");
|
||||||
|
|
||||||
// Watch Settings
|
// Watch Settings
|
||||||
final PebbleDictionary setDict = new PebbleDictionary();
|
final PebbleDictionary setDict = new PebbleDictionary();
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ import android.app.Notification;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -47,6 +49,8 @@ import android.location.Location;
|
|||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.media.ToneGenerator;
|
import android.media.ToneGenerator;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.CountDownTimer;
|
import android.os.CountDownTimer;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
@@ -123,6 +127,8 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private ToneGenerator mToneGenerator;
|
private ToneGenerator mToneGenerator;
|
||||||
|
|
||||||
|
private NetworkBroadcastReceiver mNetworkBroadcastReceiver;
|
||||||
|
|
||||||
private final IBinder mBinder = new SdBinder();
|
private final IBinder mBinder = new SdBinder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -564,6 +570,7 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
} else {
|
} else {
|
||||||
startFaultTimer();
|
startFaultTimer();
|
||||||
Log.v(TAG, "faultWarningBeep() - starting Fault Timer");
|
Log.v(TAG, "faultWarningBeep() - starting Fault Timer");
|
||||||
|
mUtil.writeToSysLogFile("faultWarningBeep() - starting Fault Timer");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -764,7 +771,7 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
// Start timer to remove the cancel audible flag
|
// Start timer to remove the cancel audible flag
|
||||||
// after the required period.
|
// after the required period.
|
||||||
if (mCancelAudibleTimer != null) {
|
if (mCancelAudibleTimer != null) {
|
||||||
Log.v(TAG, "onCreate(): cancel audible timer already running - cancelling it.");
|
Log.v(TAG, "cancelAudible(): cancel audible timer already running - cancelling it.");
|
||||||
mCancelAudibleTimer.cancel();
|
mCancelAudibleTimer.cancel();
|
||||||
mCancelAudibleTimer = null;
|
mCancelAudibleTimer = null;
|
||||||
mCancelAudible = false;
|
mCancelAudible = false;
|
||||||
@@ -792,7 +799,8 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the web server (on port 8080)
|
* Start the web server (on port 8080), and register for network connectivity events so we can log
|
||||||
|
* problems.
|
||||||
*/
|
*/
|
||||||
protected void startWebServer() {
|
protected void startWebServer() {
|
||||||
Log.v(TAG, "startWebServer()");
|
Log.v(TAG, "startWebServer()");
|
||||||
@@ -808,10 +816,16 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
} else {
|
} else {
|
||||||
Log.v(TAG, "startWebServer(): server already running???");
|
Log.v(TAG, "startWebServer(): server already running???");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mNetworkBroadcastReceiver = new NetworkBroadcastReceiver();
|
||||||
|
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||||
|
//filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||||
|
getApplicationContext().registerReceiver(mNetworkBroadcastReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the web server - FIXME - doesn't seem to do anything!
|
* Stop the web server - FIXME - doesn't seem to do anything!
|
||||||
|
* And de-register for network connectivity events.
|
||||||
*/
|
*/
|
||||||
protected void stopWebServer() {
|
protected void stopWebServer() {
|
||||||
Log.v(TAG, "SdServer.stopWebServer()");
|
Log.v(TAG, "SdServer.stopWebServer()");
|
||||||
@@ -824,8 +838,49 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
}
|
}
|
||||||
webServer = null;
|
webServer = null;
|
||||||
}
|
}
|
||||||
|
getApplicationContext().unregisterReceiver(mNetworkBroadcastReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NetworkBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Log.v(TAG, "NetworkBroadCastReceiver.onReceive");
|
||||||
|
//mUtil.writeToSysLogFile("Network State Changed" + intent.getAction());
|
||||||
|
//mUtil.showToast("Network State Changed" + intent.getAction());
|
||||||
|
|
||||||
|
ConnectivityManager cm =
|
||||||
|
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
|
||||||
|
NetworkInfo activeNetwork = null;
|
||||||
|
try {
|
||||||
|
activeNetwork = cm.getActiveNetworkInfo();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "NetworkBroadcastReceiver - failed to retrieve active network info");
|
||||||
|
mUtil.writeToSysLogFile("NetworkBroadcastReceiver - failed to retrieve active network info");
|
||||||
|
Log.v(TAG, e.toString());
|
||||||
|
}
|
||||||
|
boolean isConnected = activeNetwork != null &&
|
||||||
|
activeNetwork.isConnectedOrConnecting();
|
||||||
|
if (isConnected) {
|
||||||
|
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
|
||||||
|
if (!isWiFi) {
|
||||||
|
Log.v(TAG, "NetworkBroadcastReceiver - no Wifi Connection");
|
||||||
|
mUtil.writeToSysLogFile("Network State Changed - no Wifi Connection");
|
||||||
|
mUtil.showToast("Network State Changed - no Wifi Connection");
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "NetworkBroadcastReceiver - Wifi Connected");
|
||||||
|
mUtil.writeToSysLogFile("Network State Changed - Wifi Connected");
|
||||||
|
mUtil.showToast("Network State Changed - Wifi Connected");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "NetworkBroadcastReceiver - No Active Network");
|
||||||
|
mUtil.writeToSysLogFile("Network State Changed - No Active Network");
|
||||||
|
mUtil.showToast("Network State Changed - No Active Network");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log data to SD card if mLogData is set in preferences.
|
* Log data to SD card if mLogData is set in preferences.
|
||||||
*/
|
*/
|
||||||
@@ -1010,8 +1065,10 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
public void startFaultTimer() {
|
public void startFaultTimer() {
|
||||||
if (mFaultTimer != null) {
|
if (mFaultTimer != null) {
|
||||||
Log.v(TAG, "startFaultTimer(): fault timer already running - not doing anything.");
|
Log.v(TAG, "startFaultTimer(): fault timer already running - not doing anything.");
|
||||||
|
mUtil.writeToSysLogFile("startFaultTimer() - fault timer already running");
|
||||||
} else {
|
} else {
|
||||||
Log.v(TAG, "startFaultTimer(): starting fault timer.");
|
Log.v(TAG, "startFaultTimer(): starting fault timer.");
|
||||||
|
mUtil.writeToSysLogFile("startFaultTimer() - starting fault timer");
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
mFaultTimerCompleted = false;
|
mFaultTimerCompleted = false;
|
||||||
@@ -1027,11 +1084,13 @@ public class SdServer extends Service implements SdDataReceiver, SdLocationRecei
|
|||||||
public void stopFaultTimer() {
|
public void stopFaultTimer() {
|
||||||
if (mFaultTimer != null) {
|
if (mFaultTimer != null) {
|
||||||
Log.v(TAG, "stopFaultTimer(): fault timer already running - cancelling it.");
|
Log.v(TAG, "stopFaultTimer(): fault timer already running - cancelling it.");
|
||||||
|
mUtil.writeToSysLogFile("stopFaultTimer() - stopping fault timer");
|
||||||
mFaultTimer.cancel();
|
mFaultTimer.cancel();
|
||||||
mFaultTimer = null;
|
mFaultTimer = null;
|
||||||
mFaultTimerCompleted = false;
|
mFaultTimerCompleted = false;
|
||||||
} else {
|
} else {
|
||||||
Log.v(TAG, "stopFaultTimer(): fault timer not running - not doing anything.");
|
Log.v(TAG, "stopFaultTimer(): fault timer not running - not doing anything.");
|
||||||
|
//mUtil.writeToSysLogFile("stopFaultTimer() - fault timer not running");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -461,8 +461,8 @@ public class StartupActivity extends Activity {
|
|||||||
+ "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. "
|
+ "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. "
|
||||||
+ "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk "
|
+ "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk "
|
||||||
+ "\n\nChanges in this version:"
|
+ "\n\nChanges in this version:"
|
||||||
+ "\n- Added option to use MP3 alarm notification sounds rather than the default."
|
+ "\n- Improved logging of network status to help debugging network data source issues."
|
||||||
+ "\n- as some users had reported the default sound not working."
|
+ "\n- "
|
||||||
);
|
);
|
||||||
// This makes the links display as links, but they do not respond to clicks for some reason...
|
// This makes the links display as links, but they do not respond to clicks for some reason...
|
||||||
Linkify.addLinks(s, Linkify.ALL);
|
Linkify.addLinks(s, Linkify.ALL);
|
||||||
@@ -492,8 +492,8 @@ public class StartupActivity extends Activity {
|
|||||||
+ "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. "
|
+ "http://openseizuredetector.org.uk, or the app Facebook page at https://www.facebook.com/openseizuredetector. "
|
||||||
+ "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk "
|
+ "so I can get in touch if necessary.\nThank you! Graham \ngraham@openseizuredetector.org.uk "
|
||||||
+ "\n\nChanges in this version:"
|
+ "\n\nChanges in this version:"
|
||||||
+ "\n- Added option to use MP3 alarm notification sounds rather than the default."
|
+ "\n- Improved logging of network status to help debugging network data source issues."
|
||||||
+ "\n- as some users had reported the default sound not working."
|
+ "\n- "
|
||||||
);
|
);
|
||||||
// This makes the links display as links, but they do not respond to clicks for some reason...
|
// This makes the links display as links, but they do not respond to clicks for some reason...
|
||||||
Linkify.addLinks(s, Linkify.ALL);
|
Linkify.addLinks(s, Linkify.ALL);
|
||||||
|
|||||||
@@ -12,15 +12,15 @@
|
|||||||
android:summary="Period between server data requests in miliseconds."
|
android:summary="Period between server data requests in miliseconds."
|
||||||
android:title="Data Update Period (ms)" />
|
android:title="Data Update Period (ms)" />
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue="2000"
|
android:defaultValue="5000"
|
||||||
android:key="ConnTimeout"
|
android:key="ConnectTimeoutPeriod"
|
||||||
android:numeric="integer"
|
android:numeric="integer"
|
||||||
android:summary=""
|
android:summary=""
|
||||||
android:title="Connection Timeout (ms)" />
|
android:title="Connection Timeout Period (ms)" />
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue="2000"
|
android:defaultValue="5000"
|
||||||
android:key="SoTimeout"
|
android:key="ReadTimeoutPeriod"
|
||||||
android:numeric="integer"
|
android:numeric="integer"
|
||||||
android:summary=""
|
android:summary=""
|
||||||
android:title="So Timeout (ms)" />
|
android:title="Read Timeout Period (ms)" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user