Firebase: Allow access depending on time difference (server-side-time) - android

So I have a database that looks like this:
Posts
|
|
|----post_id_1
|
|---post_title = "...."
|---post_body = "...."
|---post_time = "ServerValue.TIME_STAMP"
|
|----post_id_2
|.......
|.......
What I want to do:
I want to prevent a user to read a post that is greater than or equal a month from the day that it was posted?
What I tried:
I tried to use this method by android:
//current time of device
long current_time = System.currentTimeMillis();
//month in millisecond
long month = .....;
if(curent_time - post_time >= month){
//this post was there for a month from the time that it was posted.
}
Problem:
If I use the above method then if a user was smart enough he/she will change the time of the device to enter a post (when they shouldn't).
Question:
Any stable way of doing this?
Thanks.
NOTE: WITHOUT GETTING THE TIME STAMP FROM THE SERVER.

I have been through the same issue, how I solved that was pinging Google for the time, and then just store that time in Firebase, that way I can compare the last time the user did something and that time will be server time and can't be changed.
So, in my case I upload that server time each time my app enters in the onStop or onDestroy method, but you can use it anywhere you need to save to your database.
Here is the snippet to get server time, I use an asyncTask in order to fetch the server time and then just post it in my reference. Just call the asyncTask where you want to update something with server time.
public class getGoogleTime extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
try {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(new HttpGet("https://google.com/"));
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.ENGLISH);
dateStr = response.getFirstHeader("Date").getValue();
Date startDate = df.parse(dateStr);
dateStr = String.valueOf(startDate.getTime() / 1000);
long actualTime = java.lang.Long.parseLong(dateStr);
//Here I do something with the Date String
} else {
//Closes the connection.
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (IOException e) {
Log.d("SAF_GTG_Response", e.getMessage());
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
// can use UI thread here
protected void onPostExecute(final Void unused) {
mDatabase.child("Posts").child(post_id_1).child("currentTime").setValue(dateStr, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
Log.d("SAF_GTG_TAG", "Success");
}
});
}
}
To work with Firebase you can do this:
ref = FirebaseDatabase.getInstance().getReference();
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
Long timestamp = (Long) snapshot.getValue();
System.out.println(timestamp);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
ref.setValue(ServerValue.TIMESTAMP);
NOTE: this method just returns UTC time, you can convert it the way you want to display it to each user.
If you are worried about network usage, just ping Google and see how many bytes and packages are being used.
Pinging google.com [172.217.162.14] with 32 bytes of data:
Reply from 172.217.162.14: bytes=32 time=17ms TTL=53
Reply from 172.217.162.14: bytes=32 time=17ms TTL=53
Reply from 172.217.162.14: bytes=32 time=17ms TTL=53
Reply from 172.217.162.14: bytes=32 time=16ms TTL=53
Ping statistics for 172.217.162.14:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 16ms, Maximum = 17ms, Average = 16ms
From the docs:
The bandwidth consumption of a simple ping command, being run one time
per second, is relatively small. The smallest ping packet possible
(including the Ethernet frame header and the IP + ICMP headers, plus
the minimum 32 bytes of the ICMP payload) takes 74 bytes
around 0,074 kilobytes.

I would store the TIME_STAMP as UTC and get the time from the server, not use the time from the device.
In that case the user cannot change the time on the device to modify the output from the database.
I would recommend to always store all dates as UTC and just convert them to the user timezone when the user views them.

Related

Problem with repeating requests in Android application

We have a problem with repeating requests. Our application (for Android) makes very simple requests to API (Ubuntu + Nginx). We have recently noticed in our logs and the Nginx server logs that very often (even 10% of users which means thousands in ou case) a single call (but not only, because it can also be a group of requests) is often called by the application even tens of thousands of times, it looks e.g. yes:
/api/v1/endpoint1 10:00:00
/api/v1/endpoint1 10:00:00
/api/v1/endpoint1 10:00:01
/api/v1/endpoint1 10:00:01
/api/v1/endpoint1 10:00:02
/api/v1/endpoint1 10:00:02
/api/v1/endpoint1 10:00:02
/api/v1/endpoint1 10:00:00
(20,000 requests)
We also noticed several groups of requests
/api/v1/endpoint1 10:00:00
/api/v1/endpoint2 10:00:00
/api/v1/endpoint3 10:00:01
/api/v1/endpoint1 10:00:01
/api/v1/endpoint2 10:00:02
/api/v1/endpoint3 10:00:02
/api/v1/endpoint1 10:00:02
/api/v1/endpoint2 10:00:00
/api/v1/endpoint3 10:00:00
It happens tens of thousands of times in a row or only 50 (but also in a row). It affects normal requests made by URLConnection.
In most cases, everything works fine - we make, for example, one request to display the results list so only one request is made. Also, on our test devices we were not able to reproduce it, everything works correctly (so one request). We would be very grateful for any suggestions and ideas how to sort it out because we seem to be at a dead end.
The problem occurs on various devices of different manufacturers, from Adnroid 4 to 9.
Code from activity (cleaned of unnecessary things):
private final ExecutorService executor = Executors.newSingleThreadExecutor();
...
public void onCreate(Bundle savedInstanceState) {
...
changeButton = (Button) findInLayout(R.id.changeButton);
changeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String data1 = data1edit.getText().toString();
final String data2 = data2edit.getText().toString();
executor.execute(new Runnable() {
#Override
public void run() {
boolean isSuccess = false;
HttpURLConnection conn;
try {
conn = (HttpURLConnection) new URL(MY_API_URL).openConnection();
conn.setRequestMethod("POST");
// setup headers, timeouts etc...
conn.setDoOutput(true);
OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
writer.write(... some post data ...);
writer.close();
conn.connect();
isSuccess = conn.getResponseCode() == 200;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
if (isSuccess) {
runOnUiThread(new Runnable() {
#Override
public void run() {
// show toast with success message + update ui
}
});
} else {
runOnUiThread(new Runnable() {
#Override
public void run() {
// show toast with error message + update ui
}
});
}
}
});
}
});
...
}
Request log screen:

How to get all google fit activities performed for a day

I know how to get various data from google Fit like steps or calories when I specifically subscribe to them.
However how can I retrieve all activities that user performed without knowing which ones they did exactly?
Also how can I get values for activities such as Stairs climbing?
It isn't available in DataType class, the samples on google developer website only show steps and calories.
Thanks
I found that I can only find all exercises using the Sessions API.
Crete a request first.
The:
.read(DataType.TYPE_WORKOUT_EXERCISE)
is important here to get all workouts.
private SessionReadRequest readFitnessSession() {
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.WEEK_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();
// Build a session read request
SessionReadRequest readRequest = new SessionReadRequest.Builder()
.setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
.read(DataType.TYPE_WORKOUT_EXERCISE)
.readSessionsFromAllApps()
.build();
// [END build_read_session_request]
return readRequest;
}
And read the sessions data
private void readSessionsApiAllSessions() {
SessionReadRequest readRequest = readFitnessSession();
Fitness.getSessionsClient(getActivity(), GoogleSignIn.getLastSignedInAccount(getActivity()))
.readSession(readRequest)
.addOnSuccessListener(new OnSuccessListener<SessionReadResponse>() {
#Override
public void onSuccess(SessionReadResponse sessionReadResponse) {
// Get a list of the sessions that match the criteria to check the result.
List<Session> sessions = sessionReadResponse.getSessions();
Log.i(TAG, "Session read was successful. Number of returned sessions is: "
+ sessions.size());
for (Session session : sessions) {
// Process the session
dumpSession(session);
// Process the data sets for this session
List<DataSet> dataSets = sessionReadResponse.getDataSet(session);
for (DataSet dataSet : dataSets) {
dumpDataSet(dataSet);
}
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i(TAG, "Failed to read session");
}
});
}

Google fit SessionAPI: SessionReadRequest is taking infinite time with no result

I'm using Google Fit API to develop a fitness app.
I'm trying to use SessionApi with SessionReadRequest to read all the sessions in the past week created with my app. Here is the code I wrote:
/**
* Creates and executes a {#link SessionReadRequest} using {#link
* com.google.android.gms.fitness.SessionsClient} to get last week sessions data
*/
private Task<SessionReadResponse> readLastWeekSessions() {
loader.smoothToShow();
// Begin by creating the query.
SessionReadRequest readRequest = readLastWeekFitnessSessions();
// [START read_session]
// Invoke the Sessions API to fetch the session with the query and wait for the result
// of the read request. Note: Fitness.SessionsApi.readSession() requires the
// ACCESS_FINE_LOCATION permission.
return Fitness.getSessionsClient(getActivity(), GoogleSignIn.getLastSignedInAccount(getContext()))
.readSession(readRequest)
.addOnCompleteListener(new OnCompleteListener<SessionReadResponse>() {
#Override
public void onComplete(#NonNull Task<SessionReadResponse> task) {
Log.i(TAG, "Session read completed");
}
})
.addOnSuccessListener(new OnSuccessListener<SessionReadResponse>() {
#Override
public void onSuccess(SessionReadResponse sessionReadResponse) {
// Get a list of the sessions that match the criteria to check the result.
List<Session> sessions = sessionReadResponse.getSessions();
Log.i(TAG, "Session read was successful. Number of returned sessions is: "
+ sessions.size());
for (Session session : sessions) {
// Process the session
dumpSession(session);
// Process the data sets for this session
List<DataSet> dataSets = sessionReadResponse.getDataSet(session);
for (DataSet dataSet : dataSets) {
dumpDataSet(dataSet);
}
}
loader.smoothToHide();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i(TAG, "Failed to read sessions");
}
});
// [END read_session]
}
/**
* Returns a {#link SessionReadRequest} for all the data of the last week sessions
*/
private SessionReadRequest readLastWeekFitnessSessions() {
Log.i(TAG, "Reading History API results for all the last week session");
// [START build_read_session_request]
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.WEEK_OF_YEAR, -1);
long startTime = cal.getTimeInMillis();
// Build a session read request
SessionReadRequest readRequest = new SessionReadRequest.Builder()
.setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
.read(DataType.TYPE_CALORIES_EXPENDED)
.read(DataType.TYPE_DISTANCE_DELTA)
.read(DataType.TYPE_SPEED)
.enableServerQueries()
.build();
// [END build_read_session_request]
return readRequest;
}
It works when I have a small amount of data (only a few sessions). But if I want to read calories burned, total distance and average speed for the last month sessions (which is around 45 sessions I created for testing), it is loading infinitely (I waited more than 10 minutes wich is unacceptable for such a request).
Is it a normal behavior? Am I supposed to destroy regularly the old sessions? Am I supposed the use aggregate data? (which I don't know how to do...)
EDIT: I guess that the problem comes from the fact that I'm getting a huge amount of datapoint for the speed which is not what I want. I want to get the average speed but don't know how to do...

Python UTF-8 text arrives deformed to Android

I want to send UTF-8 text that is stored with ElasticSeach to an application via sockets.
I have a ThreadedTCPServer Implemented, here is the class that should handle the reply.
I have implemented basic string based handshaking to share some info like query was sent and that response will be sent.
class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
es = Elasticsearch()
#receive query from the client
query = self.request.recv(1024)
#Cut off the characters that aren't recognized
query=query[2:]
#for testing
query=query.lower().strip().replace(' ','_')
print query
#Send response that query was received
self.request.send("200...OK\n")
res = es.search(index="painters",body={"query": { "match" : {"title" : query}},"size":1 })
if res['hits']['hits']:
response = res['hits']['hits'][0]['_source']['text']
self.request.send("201...RE\n")
print response
response=response.encode('utf-8')
self.request.sendall(response)
On the android side I have two functions one for reading responses and one for reading bytes.
private String getResponse(InputStream is){
String line="";
BufferedReader rd = new BufferedReader(new InputStreamReader(is),8);
try{
line=rd.readLine();
}
catch (Exception e){
Toast.makeText(MainActivity.this, "Stream Exception", Toast.LENGTH_SHORT).show();
}
return line;
}
private String convertStreamToString(InputStream is) {
BufferedInputStream bi = new BufferedInputStream(is);
byte[] b = new byte[1024];
StringBuilder total = new StringBuilder();
try {
while (bi.read(b,0,1024)!=-1)
{
total.append(decodeUTF8(b));
Log.d("TOTAL",decodeUTF8(b));
}
} catch (Exception e) {
e.printStackTrace();
}
return total.toString();
}
And here is the function that should decode the string:
String decodeUTF8(byte[] bytes) {
return new String(bytes, UTF8_CHARSET);
}
Problem is Sometimes not the whole string is shown on the android Side,
and when the whole thing goes through some UTF-8 Characters end up deformed (totally different character than sent)
AsyncTask post execute that starts new Activty:
protected void onPostExecute(String s) {
//super.onPostExecute(s);
if (s.contains("ECONNREFUSED")){
Toast.makeText(MainActivity.this,"Connection Failed",Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(MainActivity.this,ReplyActivity.class);
intent.putExtra(EXTRA_MESSAGE,s);
startActivity(intent);
}
New Intent getting the string:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get message
Intent intent = getIntent();
String summary = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
Example ouput:
Early life (1928–1949)
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), w
As you can see even when sending the query from android to python I get some crap that I need to cut off.
here:
#Cut off the characters that aren't recognized
query=query[2:]
repr(response):
<h2>Early life (1928\xe2\x80\x931949)</h2>\nAndy Warhol ("n\xc3\xa9" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889\xe2\x80\x931942) and J\xc3\xbalia ("n\xc3\xa9e" Zavack\xc3\xa1, 1892\xe2\x80\x931972), whose first child was born in their homeland and died before their move to the U.S.
Terminal print:
<h2>Early life (1928–1949)</h2>
Andy Warhol ("né" Andrej Varhola, Jr.) was born on August 6, 1928 in Pittsburgh, Pennsylvania. He was the fourth child of Andrij Warhola (Americanized as Andrew Warhola, Sr., 1889–1942) and Júlia ("née" Zavacká, 1892–1972), whose first child was born in their homeland and died before their move to the U.S.

How to get current time from internet in android

I am making an app in which I want to get the current time from internet.
I know how to get the time from the device using System.currentTimeMillis, and even after searching a lot, I did not get any clue about how to get it from internet.
You can get time from internet time servers using the below program
import java.io.IOException;
import org.apache.commons.net.time.TimeTCPClient;
public final class GetTime {
public static final void main(String[] args) {
try {
TimeTCPClient client = new TimeTCPClient();
try {
// Set timeout of 60 seconds
client.setDefaultTimeout(60000);
// Connecting to time server
// Other time servers can be found at : http://tf.nist.gov/tf-cgi/servers.cgi#
// Make sure that your program NEVER queries a server more frequently than once every 4 seconds
client.connect("time.nist.gov");
System.out.println(client.getDate());
} finally {
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.You would need Apache Commons Net library for this to work. Download the library and add to your project build path.
(Or you can also use the trimmed Apache Commons Net Library here : https://www-us.apache.org/dist//commons/net/binaries/commons-net-3.6-bin.tar.gz This is enough to get time from internet )
2.Run the program. You will get the time printed on your console.
Here is a method that i have created for you
you can use this in your code
public String getTime() {
try{
//Make the Http connection so we can retrieve the time
HttpClient httpclient = new DefaultHttpClient();
// I am using yahoos api to get the time
HttpResponse response = httpclient.execute(new
HttpGet("http://developer.yahooapis.com/TimeService/V1/getTime?appid=YahooDemo"));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
// The response is an xml file and i have stored it in a string
String responseString = out.toString();
Log.d("Response", responseString);
//We have to parse the xml file using any parser, but since i have to
//take just one value i have deviced a shortcut to retrieve it
int x = responseString.indexOf("<Timestamp>");
int y = responseString.indexOf("</Timestamp>");
//I am using the x + "<Timestamp>" because x alone gives only the start value
Log.d("Response", responseString.substring(x + "<Timestamp>".length(),y) );
String timestamp = responseString.substring(x + "<Timestamp>".length(),y);
// The time returned is in UNIX format so i need to multiply it by 1000 to use it
Date d = new Date(Long.parseLong(timestamp) * 1000);
Log.d("Response", d.toString() );
return d.toString() ;
} else{
//Closes the connection.
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
}catch (ClientProtocolException e) {
Log.d("Response", e.getMessage());
}catch (IOException e) {
Log.d("Response", e.getMessage());
}
return null;
}
If you don't care for millisecond accuracy, and if you are already using google firebase or don't mind using it (they provide a free tier), check this out: https://firebase.google.com/docs/database/android/offline-capabilities#clock-skew
Basically, firebase database has a field that provides offset value between the device time and the firebase server time. You can use this offset to get the current time.
DatabaseReference offsetRef = FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");
offsetRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
double offset = snapshot.getValue(Double.class);
double estimatedServerTimeMs = System.currentTimeMillis() + offset;
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
As I said, it will be inaccurate based on network latency.
I think the best solution is to use SNTP, in particular the SNTP client code from Android itself, e.g.:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/net/SntpClient.java/
I believe Android uses SNTP for automatic date/time updates when a cell network is not available (e.g. wifi tablets).
I think it is better then the other solutions because it uses SNTP/NTP rather then the Time protocol (RFC 868) used by the Apache TimeTCPClient. I don't know anything bad about RFC 868, but NTP is newer and seems to have superceeded it and is more widely used. I believe that Android devices that don't have cellular uses NTP.
Also, because it uses sockets. Some of the solutions proposed use HTTP so they will lose something in their accuracy.
You will need to have access to a webservice that provides current time in XML or JSON format.
If you don't find such type of service, you could parse the time from a web page, like http://www.timeanddate.com/worldclock/, or host your own time service on a server using a simple PHP page for example.
Check out JSoup for the parsing of HTML pages.
Nothing from the above worked from me. This is what I ended up with (with Volley);
This example also converts to another timezone.
Long time = null;
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.timeapi.org/utc/now";
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = simpleDateFormat.parse(response);
TimeZone tz = TimeZone.getTimeZone("Israel");
SimpleDateFormat destFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
destFormat.setTimeZone(tz);
String result = destFormat.format(date);
Log.d(TAG, "onResponse: " + result.toString());
} catch (ParseException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.w(TAG, "onErrorResponse: "+ error.getMessage());
}
});
queue.add(stringRequest);
return time;
Import Volley in gradle:
compile 'com.android.volley:volley:1.0.0'
There is a clear answer available already in Stackoverflow
https://stackoverflow.com/a/71274296/11789675
Call this url or use as GET API
http://worldtimeapi.org/api/timezone/Asia/Kolkata
the response will be like
{
"abbreviation": "IST",
"client_ip": "45.125.117.46",
"datetime": "2022-02-26T10:50:43.406519+05:30",
}
This thing works best for my apps. I use jsoup to search the google time and gets current time and then I compare the phone time with google time. So if these time are different you can stop user using a dialogbox or alertbox to tell them the times have changed. You can implement in MainActivity to check this condition. Here is a snippet so you get the idea more clearly.
public class HomeActivity extends AppCompatActivity {
//phoneDate and phoneTime to get current phone date and time
String phoneDate = new SimpleDateFormat("dd MMM yyyy ").format(clnd.getTime()).trim();
String phoneTime = new SimpleDateFormat("hh:mm a").format(clnd.getTime()).trim();
String googleDate;
String googleTime ;
#Override
protected void onCreate(Bundle _savedInstanceState) {
super.onCreate(_savedInstanceState);
setContentView(R.layout.home);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
//URL to search time
String url = "https://www.google.co.in/search?q=time";
Document document = Jsoup.connect(url).get();
org.jsoup.select.Elements time = document.getElementsByClass("gsrt vk_bk FzvWSb YwPhnf");
org.jsoup.select.Elements date = document.getElementsByClass("KfQeJ");
Log.d("HTML", "google date" + String.format(date.text()));
Log.d("HTML", "google time" + time.text());
googleDate = date.text().trim();
googleTime = time.text().trim();
//'0'is not present when hour is single digit
char second = googleTime.charAt(1);
if(second == ':'){
googleTime = "0" + googleTime;
}
Log.d("Proper format", "google time" + googleTime);
Log.d("Date", "your current url when webpage loading.." + phoneDate);
Log.d("Time", "your current url when webpage loading.." + phoneTime);
if(googleDate.contains(phoneDate) && googleTime.equals(phoneTime)){
Log.d("Time", "your current url when webpage loading.." + " true");
}else{
Log.d("Time", "your current url when webpage loading.." + " false");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
}

Categories

Resources