mirror of https://github.com/Xe/xesite_android
commit
ca74907444
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -1,48 +1,33 @@
|
|||
<!--
|
||||
Copyright 2019 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- The "package" attribute is rewritten by the Gradle build with the value of applicationId.
|
||||
It is still required here, as it is used to derive paths, for instance when referring
|
||||
to an Activity by ".MyActivity" instead of the full name. If more Activities are added to the
|
||||
application, the package attribute will need to reflect the correct path in order to use
|
||||
the abbreviated format. -->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="website.christine.xesite">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name="Application"
|
||||
android:name=".Application"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/appName"
|
||||
|
||||
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
|
||||
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<receiver android:name=".NewPostWidget">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/new_post_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="asset_statements"
|
||||
android:resource="@string/assetStatements" />
|
||||
|
||||
<meta-data
|
||||
android:name="web_manifest_url"
|
||||
android:value="@string/webManifestUrl" />
|
||||
|
||||
<meta-data
|
||||
android:name="twa_generator"
|
||||
android:value="@string/generatorApp" />
|
||||
|
@ -52,69 +37,56 @@
|
|||
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
|
||||
android:value="@string/launchUrl" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="LauncherActivity"
|
||||
android:name=".LauncherActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:label="@string/launcherName"
|
||||
android:screenOrientation="unspecified">
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.DEFAULT_URL"
|
||||
android:value="@string/launchUrl" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
|
||||
android:resource="@color/colorPrimary" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR"
|
||||
android:resource="@color/navigationColor" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR_DARK"
|
||||
android:resource="@color/navigationColorDark" />
|
||||
|
||||
<meta-data
|
||||
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR"
|
||||
android:resource="@color/navigationDividerColor" />
|
||||
|
||||
<meta-data
|
||||
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR_DARK"
|
||||
android:resource="@color/navigationDividerColorDark" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
|
||||
android:resource="@drawable/splash" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
|
||||
android:resource="@color/backgroundColor" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
|
||||
android:value="@integer/splashScreenFadeOutDuration" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
|
||||
android:value="@string/providerAuthority" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.FALLBACK_STRATEGY"
|
||||
android:value="@string/fallbackType" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SCREEN_ORIENTATION"
|
||||
android:value="@string/orientation" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
|
@ -126,9 +98,7 @@
|
|||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.FocusActivity" />
|
||||
|
||||
<activity
|
||||
android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
|
@ -147,14 +117,14 @@
|
|||
android:name=".DelegationService"
|
||||
android:enabled="@bool/enableNotification"
|
||||
android:exported="@bool/enableNotification">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.locationdelegation.PermissionRequestActivity" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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<T>(
|
||||
url: String,
|
||||
private val clazz: Class<T>,
|
||||
private val headers: MutableMap<String, String>?,
|
||||
private val listener: Response.Listener<T>,
|
||||
errorListener: Response.ErrorListener
|
||||
) : Request<T>(Method.GET, url, errorListener) {
|
||||
private val gson = Gson()
|
||||
|
||||
override fun getHeaders(): MutableMap<String, String> = headers ?: super.getHeaders()
|
||||
|
||||
override fun deliverResponse(response: T) = listener.onResponse(response)
|
||||
|
||||
override fun parseNetworkResponse(response: NetworkResponse?): Response<T> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package website.christine.xesite
|
||||
|
||||
class NewPost (
|
||||
val title: String,
|
||||
val summary: String,
|
||||
val link: String,
|
||||
)
|
|
@ -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<String, String> = mutableMapOf()
|
||||
headers.put("User-Agent", this.userAgent(ctx));
|
||||
|
||||
val jor: GsonGetRequest<NewPost> = GsonGetRequest(
|
||||
url,
|
||||
NewPost::class.java,
|
||||
headers,
|
||||
Response.Listener<NewPost> { 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)
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,59 @@
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundColor"
|
||||
android:focusable="auto"
|
||||
android:padding="@dimen/widget_margin"
|
||||
android:theme="@style/ThemeOverlay.Xesite_android.AppWidgetContainer">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="109dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="92dp"
|
||||
android:layout_height="75dp"
|
||||
android:src="@drawable/splash" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/article_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@color/backgroundColor"
|
||||
android:contentDescription="@string/appwidget_title"
|
||||
android:text="@string/appwidget_title"
|
||||
android:textColor="#3C3836"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold|italic" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/article_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="117dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@color/backgroundColor"
|
||||
android:contentDescription="@string/appwidget_body_preview"
|
||||
android:text="@string/appwidget_body_preview"
|
||||
android:textColor="#282828"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,7 @@
|
|||
<resources>
|
||||
|
||||
<style name="ThemeOverlay.Xesite_android.AppWidgetContainer" parent="">
|
||||
<item name="appWidgetBackgroundColor">@color/light_blue_900</item>
|
||||
<item name="appWidgetTextColor">@color/light_blue_200</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -0,0 +1,6 @@
|
|||
<resources>
|
||||
<declare-styleable name="AppWidgetAttrs">
|
||||
<attr name="appWidgetBackgroundColor" format="color" />
|
||||
<attr name="appWidgetTextColor" format="color" />
|
||||
</declare-styleable>
|
||||
</resources>
|
|
@ -15,4 +15,8 @@
|
|||
-->
|
||||
<resources>
|
||||
<color name="shortcut_background">#F5F5F5</color>
|
||||
<color name="light_blue_50">#FFE1F5FE</color>
|
||||
<color name="light_blue_200">#FF81D4FA</color>
|
||||
<color name="light_blue_600">#FF039BE5</color>
|
||||
<color name="light_blue_900">#FF01579B</color>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
Refer to App Widget Documentation for margin information
|
||||
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
|
||||
-->
|
||||
<dimen name="widget_margin">0dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="appwidget_title">EXAMPLE TITLE</string>
|
||||
<string name="appwidget_body_preview">These are some words that let you see what an article preview would look like.</string>
|
||||
<string name="add_widget">Add widget</string>
|
||||
<string name="new_post_channel_name">New Posts</string>
|
||||
<string name="new_post_channel_desc">Brand new posts on christine.website</string>
|
||||
</resources>
|
|
@ -0,0 +1,7 @@
|
|||
<resources>
|
||||
|
||||
<style name="ThemeOverlay.Xesite_android.AppWidgetContainer" parent="">
|
||||
<item name="appWidgetBackgroundColor">@color/light_blue_600</item>
|
||||
<item name="appWidgetTextColor">@color/light_blue_50</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialKeyguardLayout="@layout/new_post_widget"
|
||||
android:initialLayout="@layout/new_post_widget"
|
||||
android:minWidth="250dp"
|
||||
android:minHeight="80dp"
|
||||
android:previewImage="@drawable/example_appwidget_preview"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="28800000"
|
||||
android:widgetCategory="home_screen"></appwidget-provider>
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue