Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions .idea/runConfigurations.xml

This file was deleted.

22 changes: 22 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
33 changes: 30 additions & 3 deletions mobile/src/main/java/net/activitywatch/android/AssetExtractor.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
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

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(", ")}")
Expand Down Expand Up @@ -57,5 +80,9 @@ object AssetExtractor {
}
}
}

if(extractedOlder) {
setExtractedVersion(context)
}
}
}
}
35 changes: 22 additions & 13 deletions mobile/src/main/java/net/activitywatch/android/RustInterface.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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<String, Nothing, Unit>() {
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.")
Expand Down Expand Up @@ -114,4 +123,4 @@ class RustInterface constructor(context: Context? = null) {
Log.w(TAG, getBucketsJSON().toString(2))
Log.w(TAG, getEventsJSON("test").toString(2))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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) }
}
Expand Down