diff --git a/.gitignore b/.gitignore
index 159132cc..bb4a26d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ captures/
.idea/dictionaries
.idea/libraries
.idea/caches
+.idea/deploymentTargetDropDown.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 61a9130c..fb7f4a8a 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index a5f05cd8..e34606cc 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -21,5 +21,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index acbf1e13..79d4e7cc 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -45,7 +45,7 @@
-
+
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460d..00000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
index ae5f57b3..cb4f5ec8 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,27 @@ build-apk-debug: $(APKDIR)/debug/mobile-debug.apk $(APKDIR)/androidTest/debug/mo
mkdir -p dist
cp -r $(APKDIR) dist
+# Test targets
+test: test-unit
+
+test-unit:
+ ./gradlew test
+
+test-e2e:
+ # Run only screenshot test, for now
+ ./gradlew connectedAndroidTest \
+ -Pandroid.testInstrumentationRunnerArguments.class=net.activitywatch.android.ScreenshotTest
+
+test-e2e-adb:
+ # Requires that you have a device connected with the necessary APKs installed
+ # Alternative to using gradle, if you don't want to rebuild.
+ # Run only screenshot test, for now
+ adb shell pm list instrumentation
+ adb shell am instrument -w \
+ -e class net.activitywatch.android.ScreenshotTest
+ net.activitywatch.android.debug.test/androidx.test.runner.AndroidJUnitRunner
+
+# APK targets
$(APKDIR)/release/mobile-release-unsigned.apk:
TERM=xterm ./gradlew assembleRelease
tree $(APKDIR)
@@ -34,6 +55,7 @@ $(APKDIR)/androidTest/debug/mobile-debug-androidTest.apk:
TERM=xterm ./gradlew assembleAndroidTest
tree $(APKDIR)
+# Signed release APK
dist/aw-android.apk: $(APKDIR)/release/mobile-release-unsigned.apk
@# TODO: Name the APK based on the version number or commit hash.
mkdir -p dist
diff --git a/mobile/src/main/java/net/activitywatch/android/AssetExtractor.kt b/mobile/src/main/java/net/activitywatch/android/AssetExtractor.kt
index 1a81d81e..3069684d 100644
--- a/mobile/src/main/java/net/activitywatch/android/AssetExtractor.kt
+++ b/mobile/src/main/java/net/activitywatch/android/AssetExtractor.kt
@@ -1,6 +1,7 @@
package net.activitywatch.android
import android.content.Context
+import android.content.pm.PackageManager
import android.util.Log
import java.io.File
import java.io.FileOutputStream
@@ -8,9 +9,31 @@ import java.io.FileOutputStream
object AssetExtractor {
private const val TAG = "AssetExtractor"
- // TODO: Extract assets only if updated instead of using overwrite = true
+ // Returns the extracted versionCode, or 0 if it doesn't exist,
+ // used to check if we need to extract the assets again.
+ private fun getExtractedVersion(context: Context): Int {
+ val file = File(context.cacheDir, "last_extracted_version")
+ if(file.exists()) {
+ return file.readText().toInt()
+ }
+ return 0
+ }
+
+ // Sets extracted version to the current versionCode
+ private fun setExtractedVersion(context: Context) {
+ val version = BuildConfig.VERSION_CODE
+ val file = File(context.cacheDir, "last_extracted_version")
+ file.writeText(version.toString())
+ }
+
+ // Extracts all assets to the cacheDir
+ // Tries to detect updated using the versionCode,
+ // but will always extract if the file doesn't exist or if in debug mode.
// From: https://stackoverflow.com/a/8475135/965332
- fun extractAssets(path: String, context: Context, overwrite: Boolean = true) {
+ fun extractAssets(path: String, context: Context, force_overwrite: Boolean = false) {
+ val extractedOlder = getExtractedVersion(context) != BuildConfig.VERSION_CODE
+ val overwrite = force_overwrite || extractedOlder || BuildConfig.DEBUG
+
val filenames = context.assets.list(path)
//Log.w(TAG, "path: $path")
//Log.w(TAG, "files: ${filenames!!.joinToString(", ")}")
@@ -57,5 +80,9 @@ object AssetExtractor {
}
}
}
+
+ if(extractedOlder) {
+ setExtractedVersion(context)
+ }
}
-}
\ No newline at end of file
+}
diff --git a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt
index e6a9f007..e615e16c 100644
--- a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt
+++ b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt
@@ -2,14 +2,18 @@ package net.activitywatch.android
import android.content.Context
import android.os.AsyncTask
+import android.os.Handler
+import android.os.Looper
import android.system.Os
import android.util.Log
+import android.widget.Toast
import net.activitywatch.android.models.Event
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import org.threeten.bp.Instant
import java.io.File
+import java.util.concurrent.Executors
private const val TAG = "RustInterface"
@@ -51,22 +55,27 @@ class RustInterface constructor(context: Context? = null) {
fun startServerTask(context: Context) {
if(!serverStarted) {
serverStarted = true
- ServerTask(context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
+ val executor = Executors.newSingleThreadExecutor()
+ val handler = Handler(Looper.getMainLooper())
+ executor.execute {
+ // will not block the UI thread
+
+ // Extract web assets
+ AssetExtractor.extractAssets("webui", context)
+
+ // Start server
+ Log.w(TAG, "Starting server...")
+ val assetDir = context.cacheDir.path + File.separator + "webui"
+ startServer(assetDir)
+
+ handler.post {
+ // will run on UI thread after the task is done
+ }
+ }
Log.w(TAG, "Server started")
}
}
- // TODO: This probably shouldn't be an AsyncTask
- private inner class ServerTask(val context: Context) : AsyncTask() {
- override fun doInBackground(vararg inputs: String) {
- AssetExtractor.extractAssets("webui", context)
-
- Log.w(TAG, "Starting server...")
- val assetDir = context.cacheDir.path + File.separator + "webui"
- startServer(assetDir)
- }
- }
-
fun createBucketHelper(bucket_id: String, type: String, hostname: String = "unknown", client: String = "aw-android") {
if(bucket_id in getBucketsJSON().keys().asSequence()) {
Log.i(TAG, "Bucket with ID '$bucket_id', already existed. Not creating.")
@@ -114,4 +123,4 @@ class RustInterface constructor(context: Context? = null) {
Log.w(TAG, getBucketsJSON().toString(2))
Log.w(TAG, getEventsJSON("test").toString(2))
}
-}
\ No newline at end of file
+}
diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt
index 1c90c4a3..6be961f8 100644
--- a/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt
+++ b/mobile/src/main/java/net/activitywatch/android/fragments/TestFragment.kt
@@ -44,7 +44,7 @@ class TestFragment : Fragment() {
viewModel = ViewModelProviders.of(this).get(TestViewModel::class.java)
// TODO: Use the ViewModel
- val usw = UsageStatsWatcher(context!!)
+ val usw = UsageStatsWatcher(requireContext())
val button = view?.findViewById(R.id.button) as Button
button.setOnClickListener {
diff --git a/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt b/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt
index eaa59381..bb98ff27 100644
--- a/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt
+++ b/mobile/src/main/java/net/activitywatch/android/fragments/WebUIFragment.kt
@@ -16,6 +16,7 @@ import android.content.Intent.ACTION_VIEW
import android.util.Log
import android.webkit.WebViewClient
import net.activitywatch.android.R
+import java.lang.Thread.sleep
private const val TAG = "WebUI"
@@ -60,6 +61,7 @@ class WebUIFragment : Fragment() {
// Retry
// TODO: Find way to not show the blinking Android error page
Log.e(TAG, "WebView received error: $description")
+ sleep(100);
arguments?.let {
it.getString(ARG_URL)?.let { it1 -> myWebView.loadUrl(it1) }
}