diff --git a/app/build.gradle b/app/build.gradle
index c28c3cd..7f3c0a6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,6 +17,7 @@
import groovy.xml.MarkupBuilder
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
def twaManifest = [
applicationId: 'website.christine.xesite',
@@ -153,6 +154,9 @@ android {
lintOptions {
checkReleaseBuilds false
}
+ buildFeatures {
+ viewBinding true
+ }
}
task generateShorcutsFile {
@@ -202,5 +206,7 @@ dependencies {
implementation 'com.google.androidbrowserhelper:locationdelegation:1.0.0'
implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.2.0'
-
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'com.android.volley:volley:1.2.0'
+ implementation 'com.google.code.gson:gson:2.8.7'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 11d1cd4..1f68122 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,48 +1,33 @@
-
-
-
+
+
+
+
+
+
+
+
-
-
@@ -52,69 +37,56 @@
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
android:value="@string/launchUrl" />
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
@@ -126,9 +98,7 @@
android:scheme="https" />
-
-
@@ -147,14 +117,14 @@
android:name=".DelegationService"
android:enabled="@bool/enableNotification"
android:exported="@bool/enableNotification">
-
+
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/website/christine/xesite/Application.java b/app/src/main/java/website/christine/xesite/Application.kt
similarity index 78%
rename from app/src/main/java/website/christine/xesite/Application.java
rename to app/src/main/java/website/christine/xesite/Application.kt
index aa89f6a..c818d01 100644
--- a/app/src/main/java/website/christine/xesite/Application.java
+++ b/app/src/main/java/website/christine/xesite/Application.kt
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package website.christine.xesite;
+package website.christine.xesite
+import android.app.Application
-public class Application extends android.app.Application {
- @Override
- public void onCreate() {
- super.onCreate();
+class Application : Application() {
+ override fun onCreate() {
+ super.onCreate()
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/website/christine/xesite/DelegationService.java b/app/src/main/java/website/christine/xesite/DelegationService.java
deleted file mode 100644
index 252e8cb..0000000
--- a/app/src/main/java/website/christine/xesite/DelegationService.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package website.christine.xesite;
-
-import com.google.androidbrowserhelper.locationdelegation.LocationDelegationExtraCommandHandler;
-
-public class DelegationService extends
- com.google.androidbrowserhelper.trusted.DelegationService {
- @Override
- public void onCreate() {
- super.onCreate();
-
- registerExtraCommandHandler(new LocationDelegationExtraCommandHandler());
- }
-}
-
diff --git a/app/src/main/java/website/christine/xesite/DelegationService.kt b/app/src/main/java/website/christine/xesite/DelegationService.kt
new file mode 100644
index 0000000..ad467cd
--- /dev/null
+++ b/app/src/main/java/website/christine/xesite/DelegationService.kt
@@ -0,0 +1,11 @@
+package website.christine.xesite
+
+import com.google.androidbrowserhelper.locationdelegation.LocationDelegationExtraCommandHandler
+import com.google.androidbrowserhelper.trusted.DelegationService
+
+class DelegationService : DelegationService() {
+ override fun onCreate() {
+ super.onCreate()
+ registerExtraCommandHandler(LocationDelegationExtraCommandHandler())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/website/christine/xesite/GsonGetRequest.kt b/app/src/main/java/website/christine/xesite/GsonGetRequest.kt
new file mode 100644
index 0000000..f51f36e
--- /dev/null
+++ b/app/src/main/java/website/christine/xesite/GsonGetRequest.kt
@@ -0,0 +1,49 @@
+package website.christine.xesite
+
+import com.android.volley.NetworkResponse
+import com.android.volley.ParseError
+import com.android.volley.Request
+import com.android.volley.Response
+import com.android.volley.toolbox.HttpHeaderParser
+import com.google.gson.Gson
+import com.google.gson.JsonSyntaxException
+import java.io.UnsupportedEncodingException
+import java.nio.charset.Charset
+
+/**
+ * Make a GET request and return a parsed object from JSON.
+ *
+ * @param url URL of the request to make
+ * @param clazz Relevant class object, for Gson's reflection
+ * @param headers Map of request headers
+ *
+ * From here: https://developer.android.com/training/volley/request-custom
+ */
+class GsonGetRequest(
+ url: String,
+ private val clazz: Class,
+ private val headers: MutableMap?,
+ private val listener: Response.Listener,
+ errorListener: Response.ErrorListener
+) : Request(Method.GET, url, errorListener) {
+ private val gson = Gson()
+
+ override fun getHeaders(): MutableMap = headers ?: super.getHeaders()
+
+ override fun deliverResponse(response: T) = listener.onResponse(response)
+
+ override fun parseNetworkResponse(response: NetworkResponse?): Response {
+ return try {
+ val json = String(
+ response?.data ?: ByteArray(0),
+ Charset.forName(HttpHeaderParser.parseCharset(response?.headers)))
+ Response.success(
+ gson.fromJson(json, clazz),
+ HttpHeaderParser.parseCacheHeaders(response))
+ } catch (e: UnsupportedEncodingException) {
+ Response.error(ParseError(e))
+ } catch (e: JsonSyntaxException) {
+ Response.error(ParseError(e))
+ }
+ }
+}
diff --git a/app/src/main/java/website/christine/xesite/LauncherActivity.java b/app/src/main/java/website/christine/xesite/LauncherActivity.kt
similarity index 69%
rename from app/src/main/java/website/christine/xesite/LauncherActivity.java
rename to app/src/main/java/website/christine/xesite/LauncherActivity.kt
index ddf4d8f..ebf6c4e 100644
--- a/app/src/main/java/website/christine/xesite/LauncherActivity.java
+++ b/app/src/main/java/website/christine/xesite/LauncherActivity.kt
@@ -13,18 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package website.christine.xesite;
+package website.christine.xesite
-import android.net.Uri;
+import android.net.Uri
+import com.google.androidbrowserhelper.trusted.LauncherActivity
-public class LauncherActivity
- extends com.google.androidbrowserhelper.trusted.LauncherActivity {
-
- @Override
- protected Uri getLaunchingUrl() {
+class LauncherActivity : LauncherActivity() {
+ override fun getLaunchingUrl(): Uri {
// Get the original launch Url.
- Uri uri = super.getLaunchingUrl();
-
- return uri;
+ return super.getLaunchingUrl()
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/website/christine/xesite/NewPost.kt b/app/src/main/java/website/christine/xesite/NewPost.kt
new file mode 100644
index 0000000..fad5a6d
--- /dev/null
+++ b/app/src/main/java/website/christine/xesite/NewPost.kt
@@ -0,0 +1,7 @@
+package website.christine.xesite
+
+class NewPost (
+ val title: String,
+ val summary: String,
+ val link: String,
+)
\ No newline at end of file
diff --git a/app/src/main/java/website/christine/xesite/NewPostWidget.kt b/app/src/main/java/website/christine/xesite/NewPostWidget.kt
new file mode 100644
index 0000000..9b0692f
--- /dev/null
+++ b/app/src/main/java/website/christine/xesite/NewPostWidget.kt
@@ -0,0 +1,105 @@
+package website.christine.xesite
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.Context
+import android.util.Log
+import android.widget.RemoteViews
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.toolbox.BasicNetwork
+import com.android.volley.toolbox.DiskBasedCache
+import com.android.volley.toolbox.HurlStack
+
+/**
+ * Implementation of App Widget functionality.
+ */
+class NewPostWidget : AppWidgetProvider() {
+ private lateinit var requestQueue: RequestQueue
+
+ private fun userAgent(ctx: Context): String {
+ val pkgInfo = ctx.getPackageManager().getPackageInfo(ctx.packageName, 0)
+
+ return ctx.packageName.plus("/").plus(pkgInfo.versionName)
+ }
+
+ override fun onUpdate(
+ ctx: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ val url = "https://christine.website/.within/website.within.xesite/new_post"
+
+ val headers: MutableMap = mutableMapOf()
+ headers.put("User-Agent", this.userAgent(ctx));
+
+ val jor: GsonGetRequest = GsonGetRequest(
+ url,
+ NewPost::class.java,
+ headers,
+ Response.Listener { response ->
+ Log.println(Log.INFO, "new_post", response.toString())
+ // There may be multiple widgets active, so update all of them
+ for (appWidgetId in appWidgetIds) {
+ this.updateAppWidget(ctx, appWidgetManager, appWidgetId, response)
+ }
+ }, Response.ErrorListener { error ->
+ // There may be multiple widgets active, so update all of them
+ for (appWidgetId in appWidgetIds) {
+ this.updateAppWidget(
+ ctx,
+ appWidgetManager,
+ appWidgetId,
+ NewPost("Error", error.toString(), "")
+ )
+ }
+ })
+
+ if (!this::requestQueue.isInitialized) {
+ this.makeQueue(ctx)
+ }
+ this.requestQueue.add(jor)
+ }
+
+ private fun makeQueue(ctx: Context) {
+ // Instantiate the cache
+ val cache = DiskBasedCache(ctx.cacheDir, 1024 * 1024) // 1MB cap
+
+ // Set up the network to use HttpURLConnection as the HTTP client.
+ val network = BasicNetwork(HurlStack())
+
+ this.requestQueue = RequestQueue(cache, network).apply {
+ start()
+ }
+ }
+
+ override fun onEnabled(ctx: Context) {
+ this.makeQueue(ctx)
+ }
+
+ override fun onDisabled(context: Context) {
+ // Enter relevant functionality for when the last widget is disabled
+ }
+
+ private fun updateAppWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int,
+ body: NewPost
+ ) {
+ // Construct the RemoteViews object
+ val views = RemoteViews(context.packageName, R.layout.new_post_widget)
+
+ views.setTextViewText(
+ R.id.article_title,
+ body.title
+ )
+ views.setTextViewText(
+ R.id.article_preview,
+ body.summary
+ )
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/app/src/main/res/drawable-nodpi/example_appwidget_preview.png
new file mode 100644
index 0000000..5861a0e
Binary files /dev/null and b/app/src/main/res/drawable-nodpi/example_appwidget_preview.png differ
diff --git a/app/src/main/res/layout/new_post_widget.xml b/app/src/main/res/layout/new_post_widget.xml
new file mode 100644
index 0000000..0a7a166
--- /dev/null
+++ b/app/src/main/res/layout/new_post_widget.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..cde4114
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..97531a2
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index e66222d..58b6495 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -15,4 +15,8 @@
-->
#F5F5F5
+ #FFE1F5FE
+ #FF81D4FA
+ #FF039BE5
+ #FF01579B
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..4db8c59
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..9f580c4
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+
+ EXAMPLE TITLE
+ These are some words that let you see what an article preview would look like.
+ Add widget
+ New Posts
+ Brand new posts on christine.website
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..911b6b4
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/new_post_widget_info.xml b/app/src/main/res/xml/new_post_widget_info.xml
new file mode 100644
index 0000000..b365135
--- /dev/null
+++ b/app/src/main/res/xml/new_post_widget_info.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7a99ebf..0d68f0a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,12 +18,16 @@
buildscript {
+ ext {
+ kotlin_version = '1.5.0'
+ }
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files