Skip to content

Add Widget and Worker and Room #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 21, 2025
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
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ Shows a preview with custom options and copies the URL to the clipboard after up

</details>

> [!WARNING]
> Google Play is currently in Closed Testing.
> To be included see [this discussion](https://github.com/cssnr/zipline-android/discussions/25).

[![Google Play](https://raw.githubusercontent.com/smashedr/repo-images/refs/heads/master/google/get-on-play-400.webp)](https://play.google.com/store/apps/details?id=org.cssnr.zipline)

_Note: Until published on the play store, you may need to allow installation of apps from unknown sources._

Downloading and Installing the [apk](https://github.com/cssnr/zipline-android/releases/latest/download/app-release.apk)
Expand Down Expand Up @@ -85,6 +91,7 @@ Additionally, the URL is copied to the clipboard and the preview is show in the
- Share or Open any file or URL to your Zipline server.
- Single file previews most media with custom name option.
- Multiple file upload previews, options and file selector.
- Widget with stats, custom update interval, upload button.

### Planned

Expand Down Expand Up @@ -203,13 +210,13 @@ For more details, see the [ADB Documentation](https://developer.android.com/tool
## Google Services

This app uses Firebase Google Services. Building requires a valid `google-services.json` file in the `app` directory.
You must add `org.cssnr.zipline.dev` to a Firebase campaign here: https://firebase.google.com/
You must add `org.cssnr.zipline` to a Firebase campaign here: https://firebase.google.com/

To enable/disable Firebase DebugView use the following commands:

```shell
# set
adb shell setprop debug.firebase.analytics.app org.cssnr.zipline.dev
adb shell setprop debug.firebase.analytics.app org.cssnr.zipline

# unset
adb shell setprop debug.firebase.analytics.app .none.
Expand Down
6 changes: 5 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
//alias(libs.plugins.ksp)
alias(libs.plugins.ksp)
alias(libs.plugins.google.services)
alias(libs.plugins.firebase.crashlytics)
}
Expand Down Expand Up @@ -62,6 +62,9 @@ dependencies {
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.androidx.preference.ktx)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.room.ktx)
implementation(libs.androidx.room.runtime)
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.firebase.crashlytics)
Expand All @@ -74,6 +77,7 @@ dependencies {
implementation(libs.media3.ui)
implementation(libs.media3.ui.compose)
implementation(libs.taptargetview)
ksp(libs.androidx.room.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
3 changes: 2 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile

# Logging

## Logging
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@
android:name="firebase_crashlytics_collection_enabled"
android:value="${firebaseCrashlyticsEnabled}" />

<activity android:name=".widget.WidgetConfiguration"
android:theme="@style/Theme.WidgetConfig.Transparent"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>

<receiver android:name=".widget.WidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget" />
</receiver>

</application>

</manifest>
66 changes: 56 additions & 10 deletions app/src/main/java/org/cssnr/zipline/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.cssnr.zipline

import android.annotation.SuppressLint
import android.appwidget.AppWidgetManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Context.CLIPBOARD_SERVICE
import android.content.Intent
Expand All @@ -24,8 +26,15 @@ import androidx.navigation.NavOptions
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI
import androidx.preference.PreferenceManager
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import org.cssnr.zipline.databinding.ActivityMainBinding
import org.cssnr.zipline.widget.WidgetProvider
import org.cssnr.zipline.work.APP_WORKER_CONSTRAINTS
import org.cssnr.zipline.work.AppWorker
import java.net.URL
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

Expand Down Expand Up @@ -60,6 +69,27 @@ class MainActivity : AppCompatActivity() {

// Set Default Preferences
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
PreferenceManager.setDefaultValues(this, R.xml.preferences_widget, false)

// TODO: Improve initialization of the WorkRequest
val workInterval = preferences.getString("work_interval", null) ?: "0"
Log.i("Main[onCreate]", "workInterval: $workInterval")
if (workInterval != "0") {
val workRequest =
PeriodicWorkRequestBuilder<AppWorker>(workInterval.toLong(), TimeUnit.MINUTES)
.setConstraints(APP_WORKER_CONSTRAINTS)
.build()
Log.i("Main[onCreate]", "workRequest: $workRequest")
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"app_worker",
ExistingPeriodicWorkPolicy.KEEP,
workRequest
)
} else {
// TODO: Confirm this is necessary...
Log.i("Main[onCreate]", "Ensuring Work is Disabled")
WorkManager.getInstance(this).cancelUniqueWork("app_worker")
}

// Handle Custom Navigation Items
binding.navView.setNavigationItemSelectedListener { menuItem ->
Expand Down Expand Up @@ -102,12 +132,13 @@ class MainActivity : AppCompatActivity() {

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
Log.d("onNewIntent", "intent.data: ${intent.data}")
val action = intent.action
Log.d("onNewIntent", "action: $action")
Log.d("onNewIntent", "data: ${intent.data}")
Log.d("onNewIntent", "intent.type: ${intent.type}")
Log.d("onNewIntent", "intent.action: ${intent.action}")

val extraText = intent.getStringExtra(Intent.EXTRA_TEXT)
Log.d("onNewIntent", "extraText: $extraText")
Log.d("onNewIntent", "extraText: ${extraText?.take(100)}")

val savedUrl = preferences.getString("ziplineUrl", null)
val authToken = preferences.getString("ziplineToken", null)
Expand All @@ -123,7 +154,7 @@ class MainActivity : AppCompatActivity() {
.build()
)

} else if (Intent.ACTION_MAIN == intent.action) {
} else if (Intent.ACTION_MAIN == action) {
Log.d("onNewIntent", "ACTION_MAIN")

binding.drawerLayout.closeDrawers()
Expand Down Expand Up @@ -157,7 +188,7 @@ class MainActivity : AppCompatActivity() {
filePickerLauncher.launch(arrayOf("*/*"))
}

} else if (Intent.ACTION_SEND == intent.action) {
} else if (Intent.ACTION_SEND == action) {
Log.d("onNewIntent", "ACTION_SEND")

val fileUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Expand All @@ -169,7 +200,7 @@ class MainActivity : AppCompatActivity() {
Log.d("onNewIntent", "File URI: $fileUri")

if (fileUri == null && !extraText.isNullOrEmpty()) {
Log.d("onNewIntent", "SEND TEXT DETECTED: $extraText")
Log.d("onNewIntent", "SEND TEXT DETECTED: ${extraText.take(100)}")
//if (extraText.lowercase().startsWith("http")) {
//if (Patterns.WEB_URL.matcher(extraText).matches()) {
if (isURL(extraText)) {
Expand Down Expand Up @@ -204,7 +235,7 @@ class MainActivity : AppCompatActivity() {
showPreview(fileUri)
}

} else if (Intent.ACTION_SEND_MULTIPLE == intent.action) {
} else if (Intent.ACTION_SEND_MULTIPLE == action) {
Log.d("onNewIntent", "ACTION_SEND_MULTIPLE")

val fileUris = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Expand All @@ -221,18 +252,33 @@ class MainActivity : AppCompatActivity() {
}
showMultiPreview(fileUris)

} else if (Intent.ACTION_VIEW == intent.action) {
} else if (Intent.ACTION_VIEW == action) {
Log.d("onNewIntent", "ACTION_VIEW")

Log.d("onNewIntent", "File URI: ${intent.data}")
showPreview(intent.data)

} else if ("UPLOAD_FILE" == action) {
Log.d("handleIntent", "UPLOAD_FILE")

filePickerLauncher.launch(arrayOf("*/*"))

} else {
Toast.makeText(this, "That's a Bug!", Toast.LENGTH_SHORT).show()
Log.w("onNewIntent", "BUG: UNKNOWN intent.action: ${intent.action}")
Toast.makeText(this, "That's a Bug!", Toast.LENGTH_LONG).show()
Log.w("onNewIntent", "UNKNOWN INTENT - action: $action")
}
}

override fun onStop() {
super.onStop()
Log.d("Main[onStop]", "MainActivity - onStop")
// Update Widget
val appWidgetManager = AppWidgetManager.getInstance(this)
val componentName = ComponentName(this, WidgetProvider::class.java)
val ids = appWidgetManager.getAppWidgetIds(componentName)
WidgetProvider().onUpdate(this, appWidgetManager, ids)
}

private fun isURL(url: String): Boolean {
return try {
URL(url)
Expand Down
Loading
Loading