From e9b265b06f789311bfd5668971d9117db9565ba2 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Thu, 12 Sep 2024 12:20:09 +0900 Subject: [PATCH 01/14] feat: add sample widget --- android/app/build.gradle.kts | 3 + android/app/src/main/AndroidManifest.xml | 76 ++++++++++----- .../org/sparcs/otlplus/OTLTimelineWidget.kt | 52 ++++++++++ .../OTLTimelineWidgetConfigureActivity.kt | 92 ++++++++++++++++++ .../example_appwidget_preview.png | Bin 0 -> 3522 bytes .../drawable-v21/app_widget_background.xml | 10 ++ .../app_widget_inner_view_background.xml | 10 ++ .../main/res/layout/o_t_l_timeline_widget.xml | 19 ++++ .../o_t_l_timeline_widget_configure.xml | 28 ++++++ .../src/main/res/values-night-v31/themes.xml | 10 ++ .../app/src/main/res/values-v21/styles.xml | 14 +++ .../app/src/main/res/values-v31/styles.xml | 14 +++ .../app/src/main/res/values-v31/themes.xml | 11 +++ android/app/src/main/res/values/attrs.xml | 7 ++ android/app/src/main/res/values/colors.xml | 4 + android/app/src/main/res/values/dimens.xml | 10 ++ android/app/src/main/res/values/strings.xml | 7 ++ android/app/src/main/res/values/styles.xml | 27 +++-- android/app/src/main/res/values/themes.xml | 17 ++++ .../res/xml/o_t_l_timeline_widget_info.xml | 15 +++ android/build.gradle.kts | 8 ++ 21 files changed, 401 insertions(+), 33 deletions(-) create mode 100644 android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidget.kt create mode 100644 android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidgetConfigureActivity.kt create mode 100644 android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png create mode 100644 android/app/src/main/res/drawable-v21/app_widget_background.xml create mode 100644 android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml create mode 100644 android/app/src/main/res/layout/o_t_l_timeline_widget.xml create mode 100644 android/app/src/main/res/layout/o_t_l_timeline_widget_configure.xml create mode 100644 android/app/src/main/res/values-night-v31/themes.xml create mode 100644 android/app/src/main/res/values-v21/styles.xml create mode 100644 android/app/src/main/res/values-v31/themes.xml create mode 100644 android/app/src/main/res/values/attrs.xml create mode 100644 android/app/src/main/res/values/dimens.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/themes.xml create mode 100644 android/app/src/main/res/xml/o_t_l_timeline_widget_info.xml diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 71677bbf..4078279f 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -80,6 +80,9 @@ android { kotlinOptions { jvmTarget = "17" } + buildFeatures { + viewBinding = true + } } flutter { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index df6bec9f..ea0dcde3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,49 +1,75 @@ + - - + FlutterApplication and put your custom class here. + --> + - - + android:maxSdkVersion="28" /> + + + + android:fullBackupContent="true" + android:icon="@mipmap/ic_launcher" + android:label="OTL"> + + + + + + + + + + + + + + android:windowSoftInputMode="adjustResize"> - - + + + - + + - + - + + android:value="2" /> + \ No newline at end of file diff --git a/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidget.kt b/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidget.kt new file mode 100644 index 00000000..9cc8952f --- /dev/null +++ b/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidget.kt @@ -0,0 +1,52 @@ +package org.sparcs.otlplus + +import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetProvider +import android.content.Context +import android.widget.RemoteViews + +/** + * Implementation of App Widget functionality. + * App Widget Configuration implemented in [OTLTimelineWidgetConfigureActivity] + */ +class OTLTimelineWidget : AppWidgetProvider() { + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + // There may be multiple widgets active, so update all of them + for (appWidgetId in appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId) + } + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + // When the user deletes the widget, delete the preference associated with it. + for (appWidgetId in appWidgetIds) { + deleteTitlePref(context, appWidgetId) + } + } + + override fun onEnabled(context: Context) { + // Enter relevant functionality for when the first widget is created + } + + override fun onDisabled(context: Context) { + // Enter relevant functionality for when the last widget is disabled + } +} + +internal fun updateAppWidget( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetId: Int +) { + val widgetText = loadTitlePref(context, appWidgetId) + // Construct the RemoteViews object + val views = RemoteViews(context.packageName, R.layout.o_t_l_timeline_widget) + views.setTextViewText(R.id.appwidget_text, widgetText) + + // Instruct the widget manager to update the widget + appWidgetManager.updateAppWidget(appWidgetId, views) +} \ No newline at end of file diff --git a/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidgetConfigureActivity.kt b/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidgetConfigureActivity.kt new file mode 100644 index 00000000..cec64eab --- /dev/null +++ b/android/app/src/main/java/org/sparcs/otlplus/OTLTimelineWidgetConfigureActivity.kt @@ -0,0 +1,92 @@ +package org.sparcs.otlplus + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.widget.EditText +import org.sparcs.otlplus.databinding.OTLTimelineWidgetConfigureBinding + +/** + * The configuration screen for the [OTLTimelineWidget] AppWidget. + */ +class OTLTimelineWidgetConfigureActivity : Activity() { + private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + private lateinit var appWidgetText: EditText + private var onClickListener = View.OnClickListener { + val context = this@OTLTimelineWidgetConfigureActivity + + // When the button is clicked, store the string locally + val widgetText = appWidgetText.text.toString() + saveTitlePref(context, appWidgetId, widgetText) + + // It is the responsibility of the configuration activity to update the app widget + val appWidgetManager = AppWidgetManager.getInstance(context) + updateAppWidget(context, appWidgetManager, appWidgetId) + + // Make sure we pass back the original appWidgetId + val resultValue = Intent() + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + setResult(RESULT_OK, resultValue) + finish() + } + private lateinit var binding: OTLTimelineWidgetConfigureBinding + + public override fun onCreate(icicle: Bundle?) { + super.onCreate(icicle) + + // Set the result to CANCELED. This will cause the widget host to cancel + // out of the widget placement if the user presses the back button. + setResult(RESULT_CANCELED) + + binding = OTLTimelineWidgetConfigureBinding.inflate(layoutInflater) + setContentView(binding.root) + + appWidgetText = binding.appwidgetText as EditText + binding.addButton.setOnClickListener(onClickListener) + + // Find the widget id from the intent. + val intent = intent + val extras = intent.extras + if (extras != null) { + appWidgetId = extras.getInt( + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID + ) + } + + // If this activity was started with an intent without an app widget ID, finish with an error. + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + return + } + + appWidgetText.setText(loadTitlePref(this@OTLTimelineWidgetConfigureActivity, appWidgetId)) + } + +} + +private const val PREFS_NAME = "org.sparcs.otlplus.OTLTimelineWidget" +private const val PREF_PREFIX_KEY = "appwidget_" + +// Write the prefix to the SharedPreferences object for this widget +internal fun saveTitlePref(context: Context, appWidgetId: Int, text: String) { + val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() + prefs.putString(PREF_PREFIX_KEY + appWidgetId, text) + prefs.apply() +} + +// Read the prefix from the SharedPreferences object for this widget. +// If there is no preference saved, get the default from a resource +internal fun loadTitlePref(context: Context, appWidgetId: Int): String { + val prefs = context.getSharedPreferences(PREFS_NAME, 0) + val titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null) + return titleValue ?: context.getString(R.string.appwidget_text) +} + +internal fun deleteTitlePref(context: Context, appWidgetId: Int) { + val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() + prefs.remove(PREF_PREFIX_KEY + appWidgetId) + prefs.apply() +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png b/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..894b069a4907d258f60b1b2406b90f5a0fe1c35b GIT binary patch literal 3522 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 + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml new file mode 100644 index 00000000..007e2872 --- /dev/null +++ b/android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/o_t_l_timeline_widget.xml b/android/app/src/main/res/layout/o_t_l_timeline_widget.xml new file mode 100644 index 00000000..7a7bbac5 --- /dev/null +++ b/android/app/src/main/res/layout/o_t_l_timeline_widget.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/o_t_l_timeline_widget_configure.xml b/android/app/src/main/res/layout/o_t_l_timeline_widget_configure.xml new file mode 100644 index 00000000..af0b9d72 --- /dev/null +++ b/android/app/src/main/res/layout/o_t_l_timeline_widget_configure.xml @@ -0,0 +1,28 @@ + + + + + + + +