Skip to content

Commit

Permalink
Merge pull request #56 from jogrimst/master
Browse files Browse the repository at this point in the history
#55 Upload ApplicationOpenedEvents to server
  • Loading branch information
literacyapp authored Jun 7, 2017
2 parents e5348fc + 68ab6ab commit 0a79b90
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 41 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ android:
- android-23
- extra-android-m2repository

#notifications:
# slack: literacyapp:HLjtHJdZ7DYJV2DlnDLrG6Gl
notifications:
email: false
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
applicationId "org.literacyapp.analytics"
minSdkVersion 21
targetSdkVersion 23
versionCode 1001000
versionName "1.1.0"
versionCode 1002000
versionName "1.2.0"
}

buildTypes {
Expand Down
20 changes: 7 additions & 13 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
package="org.literacyapp.analytics">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:name=".AnalyticsApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">

<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -29,72 +31,64 @@
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".service.ServerSynchronizationJobService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />

<receiver
android:name=".receiver.StudentUpdatedReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.STUDENT_UPDATED" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.ApplicationOpenedEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.APPLICATION_OPENED_EVENT" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.LetterLearningEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.LETTER_LEARNING_EVENT" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.NumberLearningEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.NUMBER_LEARNING_EVENT" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.VideoLearningEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.VIDEO_LEARNING_EVENT" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.StoryBookLearningEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.STORYBOOK_LEARNING_EVENT" />
</intent-filter>
</receiver>

<receiver
android:name=".receiver.ShapeLearningEventReceiver"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="literacyapp.intent.action.SHAPE_LEARNING_EVENT" />
</intent-filter>
Expand Down
33 changes: 24 additions & 9 deletions app/src/main/java/org/literacyapp/analytics/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.widget.Toast;

import org.literacyapp.analytics.service.ScreenshotJobService;
import org.literacyapp.analytics.service.ServerSynchronizationJobService;
import org.literacyapp.analytics.util.RootHelper;

public class MainActivity extends Activity {
Expand Down Expand Up @@ -61,20 +62,34 @@ public void onClick(View view) {
if (!isRootPermissionGranted) {
Toast.makeText(getApplicationContext(), "Root permission was not granted. Please see log for details.", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Root permission was granted. Starting background job...", Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), "Root permission was granted. Starting background jobs...", Toast.LENGTH_LONG).show();

// Initiate background job for recording screenshots
ComponentName componentName = new ComponentName(getApplicationContext(), ScreenshotJobService.class);
// Initiate background job for synchronizing events with server
// Note: This code block also exists in the BootReceiver
ComponentName componentName = new ComponentName(getApplicationContext(), ServerSynchronizationJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(1, componentName);
builder.setPeriodic(60 * 60 * 1000); // Every 60 minutes
JobInfo serverSynchronizationJobInfo = builder.build();
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int resultId = jobScheduler.schedule(serverSynchronizationJobInfo);
if (resultId == JobScheduler.RESULT_SUCCESS) {
Log.i(getClass().getName(), "Server synchronization Job scheduled with id: " + serverSynchronizationJobInfo.getId());
} else {
Log.w(getClass().getName(), "Server synchronization Job scheduling failed. JobInfo id: " + serverSynchronizationJobInfo.getId());
}


// Initiate background job for recording screenshots
// Note: This code block also exists in the BootReceiver
componentName = new ComponentName(getApplicationContext(), ScreenshotJobService.class);
builder = new JobInfo.Builder(2, componentName);
builder.setPeriodic(5 * 60 * 1000); // Every 5 minutes
JobInfo screenshotJobInfo = builder.build();

JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int resultId = jobScheduler.schedule(screenshotJobInfo);
if (resultId > 0) {
Log.i(getClass().getName(), "Job scheduled with id: " + resultId);
resultId = jobScheduler.schedule(screenshotJobInfo);
if (resultId == JobScheduler.RESULT_SUCCESS) {
Log.i(getClass().getName(), "Screenshot Job scheduled with id: " + screenshotJobInfo.getId());
} else {
Log.w(getClass().getName(), "Job scheduling failed. Error id: " + resultId);
Log.w(getClass().getName(), "Screenshot Job scheduling failed. JobInfo id: " + serverSynchronizationJobInfo.getId());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* Master of DAO (schema version 1001000): knows all DAOs.
* Master of DAO (schema version 1002000): knows all DAOs.
*/
public class DaoMaster extends AbstractDaoMaster {
public static final int SCHEMA_VERSION = 1001000;
public static final int SCHEMA_VERSION = 1002000;

/** Creates underlying database table using DAOs. */
public static void createAllTables(Database db, boolean ifNotExists) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.literacyapp.analytics.dao.BootCompletedEventDao;
import org.literacyapp.analytics.model.BootCompletedEvent;
import org.literacyapp.analytics.service.ScreenshotJobService;
import org.literacyapp.analytics.service.ServerSynchronizationJobService;
import org.literacyapp.analytics.util.DeviceInfoHelper;

import java.io.File;
Expand All @@ -28,19 +29,6 @@ public class BootReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i(getClass().getName(), "onReceive");

// Initiate background job for recording screenshots
ComponentName componentName = new ComponentName(context, ScreenshotJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(1, componentName);
builder.setPeriodic(5 * 60 * 1000); // Every 5 minutes
JobInfo screenshotJobInfo = builder.build();
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int resultId = jobScheduler.schedule(screenshotJobInfo);
if (resultId > 0) {
Log.i(getClass().getName(), "Job scheduled with id: " + resultId);
} else {
Log.w(getClass().getName(), "Job scheduling failed. Error id: " + resultId);
}


// Store event in database
BootCompletedEvent bootCompletedEvent = new BootCompletedEvent();
Expand Down Expand Up @@ -73,5 +61,34 @@ public void onReceive(Context context, Intent intent) {
} catch (IOException e) {
Log.e(getClass().getName(), null, e);
}


// Initiate background job for synchronizing events with server
// Note: This code block also exists in the MainActivity
ComponentName componentName = new ComponentName(context, ServerSynchronizationJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(1, componentName);
builder.setPeriodic(60 * 60 * 1000); // Every 60 minutes
JobInfo serverSynchronizationJobInfo = builder.build();
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int resultId = jobScheduler.schedule(serverSynchronizationJobInfo);
if (resultId == JobScheduler.RESULT_SUCCESS) {
Log.i(getClass().getName(), "Server synchronization Job scheduled with id: " + serverSynchronizationJobInfo.getId());
} else {
Log.w(getClass().getName(), "Server synchronization Job scheduling failed. JobInfo id: " + serverSynchronizationJobInfo.getId());
}


// Initiate background job for recording screenshots
// Note: This code block also exists in the MainActivity
componentName = new ComponentName(context, ScreenshotJobService.class);
builder = new JobInfo.Builder(2, componentName);
builder.setPeriodic(5 * 60 * 1000); // Every 5 minutes
JobInfo screenshotJobInfo = builder.build();
resultId = jobScheduler.schedule(screenshotJobInfo);
if (resultId == JobScheduler.RESULT_SUCCESS) {
Log.i(getClass().getName(), "Screenshot Job scheduled with id: " + screenshotJobInfo.getId());
} else {
Log.w(getClass().getName(), "Screenshot Job scheduling failed. JobInfo id: " + serverSynchronizationJobInfo.getId());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

/**
* Service responsible for recording screenshots when the screen in switched on.
*
* This service is triggered in the @{link {@link org.literacyapp.analytics.receiver.BootReceiver}}
*/
public class ScreenshotJobService extends JobService {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.literacyapp.analytics.service;

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.os.Environment;
import android.util.Log;

import org.literacyapp.analytics.util.EnvironmentSettings;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
* Service responsible for uploading event files to server.
*
* This service is triggered in the @{link {@link org.literacyapp.analytics.receiver.BootReceiver}}
*/
public class ServerSynchronizationJobService extends JobService {

@Override
public boolean onStartJob(JobParameters jobParameters) {
Log.i(getClass().getName(), "onStartJob");

String logsPath = Environment.getExternalStorageDirectory() + "/.literacyapp-analytics/events";
File eventsDir = new File(logsPath);
Log.i(getClass().getName(), "eventsDir: " + eventsDir);
Log.i(getClass().getName(), "eventsDir.exists(): " + eventsDir.exists());
if (eventsDir.exists()) {
File[] deviceDirs = eventsDir.listFiles();
for (int i = 0; i < deviceDirs.length; i++) {
File deviceDir = deviceDirs[i];
Log.i(getClass().getName(), "deviceDir: " + deviceDir);

File[] eventFiles = deviceDir.listFiles();
for (int j = 0; j < eventFiles.length; j++) {
File eventFile = eventFiles[j];
Log.i(getClass().getName(), "eventFile: " + eventFile);
// Expected filename: "application_opened_events_yyyy-MM-dd.log"

if (eventFile.getName().startsWith("application_opened_events_")) {
// Skip files generated more than 7 days ago
String dateAsString = eventFile.getName().replace("application_opened_events_", "").replace(".log", "");
Log.i(getClass().getName(), "dateAsString: " + dateAsString);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = simpleDateFormat.parse(dateAsString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
Log.i(getClass().getName(), "calendar.getTime(): " + calendar.getTime());

Calendar calendar7DaysAgo = Calendar.getInstance();
calendar7DaysAgo.setTime(calendar.getTime());
calendar7DaysAgo.add(Calendar.DAY_OF_MONTH, -7);
Log.i(getClass().getName(), "calendar7DaysAgo.getTime(): " + calendar7DaysAgo.getTime());

if (!calendar.before(calendar7DaysAgo)) {
Log.i(getClass().getName(), "Uploading to web server: " + eventFile);

new UploadApplicationOpenedEventsAsyncTask().execute(eventFile);
}
} catch (ParseException e) {
Log.e(getClass().getName(), null, e);
}
}
}
}
}

boolean asynchronousProcessing = false;
return asynchronousProcessing;
}

@Override
public boolean onStopJob(JobParameters jobParameters) {
Log.i(getClass().getName(), "onStopJob");

boolean restartJob = false;
return restartJob;
}
}
Loading

0 comments on commit 0a79b90

Please sign in to comment.