diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java
deleted file mode 100644
index 29cd9bcc..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package org.moire.ultrasonic.provider;
-
-import android.app.PendingIntent;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProvider;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Environment;
-import android.view.KeyEvent;
-import android.widget.RemoteViews;
-
-import org.moire.ultrasonic.R;
-import org.moire.ultrasonic.activity.NavigationActivity;
-import org.moire.ultrasonic.domain.Track;
-import org.moire.ultrasonic.imageloader.BitmapUtils;
-import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver;
-import org.moire.ultrasonic.service.MediaPlayerController;
-import org.moire.ultrasonic.util.Constants;
-
-import timber.log.Timber;
-
-/**
- * Widget Provider for the Ultrasonic Widgets
- */
-public class UltrasonicAppWidgetProvider extends AppWidgetProvider
-{
- protected int layoutId;
-
- @Override
- public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
- {
- defaultAppWidget(context, appWidgetIds);
- }
-
- /**
- * Initialize given widgets to default state, where we launch Ultrasonic on default click
- * and hide actions if service not running.
- */
- private void defaultAppWidget(Context context, int[] appWidgetIds)
- {
- final Resources res = context.getResources();
- final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);
-
- views.setTextViewText(R.id.title, null);
- views.setTextViewText(R.id.album, null);
- views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text));
-
- linkButtons(context, views, false);
- pushUpdate(context, appWidgetIds, views);
- }
-
- private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views)
- {
- // Update specific list of appWidgetIds if given, otherwise default to all
- final AppWidgetManager manager = AppWidgetManager.getInstance(context);
-
- if (manager != null)
- {
- if (appWidgetIds != null)
- {
- manager.updateAppWidget(appWidgetIds, views);
- }
- else
- {
- manager.updateAppWidget(new ComponentName(context, this.getClass()), views);
- }
- }
- }
-
- /**
- * Handle a change notification coming over from {@link MediaPlayerController}
- */
- public void notifyChange(Context context, Track currentSong, boolean playing, boolean setAlbum)
- {
- if (hasInstances(context))
- {
- performUpdate(context, currentSong, playing, setAlbum);
- }
- }
-
- /**
- * Check against {@link AppWidgetManager} if there are any instances of this widget.
- */
- private boolean hasInstances(Context context)
- {
- AppWidgetManager manager = AppWidgetManager.getInstance(context);
-
- if (manager != null)
- {
- int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));
- return (appWidgetIds.length > 0);
- }
-
- return false;
- }
-
- /**
- * Update all active widget instances by pushing changes
- */
- private void performUpdate(Context context, Track currentSong, boolean playing, boolean setAlbum)
- {
- final Resources res = context.getResources();
- final RemoteViews views = new RemoteViews(context.getPackageName(), this.layoutId);
-
- String title = currentSong == null ? null : currentSong.getTitle();
- String artist = currentSong == null ? null : currentSong.getArtist();
- String album = currentSong == null ? null : currentSong.getAlbum();
- CharSequence errorState = null;
-
- // Show error message?
- String status = Environment.getExternalStorageState();
- if (status.equals(Environment.MEDIA_SHARED) || status.equals(Environment.MEDIA_UNMOUNTED))
- {
- errorState = res.getText(R.string.widget_sdcard_busy);
- }
- else if (status.equals(Environment.MEDIA_REMOVED))
- {
- errorState = res.getText(R.string.widget_sdcard_missing);
- }
- else if (currentSong == null)
- {
- errorState = res.getText(R.string.widget_initial_text);
- }
-
- if (errorState != null)
- {
- // Show error state to user
- views.setTextViewText(R.id.title, null);
- views.setTextViewText(R.id.artist, errorState);
- if (setAlbum)
- {
- views.setTextViewText(R.id.album, null);
- }
- views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album);
- }
- else
- {
- // No error, so show normal titles
- views.setTextViewText(R.id.title, title);
- views.setTextViewText(R.id.artist, artist);
- if (setAlbum)
- {
- views.setTextViewText(R.id.album, album);
- }
- }
-
- // Set correct drawable for pause state
- if (playing)
- {
- views.setImageViewResource(R.id.control_play, R.drawable.media_pause_normal_dark);
- }
- else
- {
- views.setImageViewResource(R.id.control_play, R.drawable.media_start_normal_dark);
- }
-
- // Set the cover art
- try
- {
- Bitmap bitmap = currentSong == null ? null : BitmapUtils.Companion.getAlbumArtBitmapFromDisk(currentSong, 240);
-
- if (bitmap == null)
- {
- // Set default cover art
- views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album);
- }
- else
- {
- views.setImageViewBitmap(R.id.appwidget_coverart, bitmap);
- }
- }
- catch (Exception x)
- {
- Timber.e(x, "Failed to load cover art");
- views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album);
- }
-
- // Link actions buttons to intents
- linkButtons(context, views, currentSong != null);
-
- pushUpdate(context, null, views);
- }
-
- /**
- * Link up various button actions using {@link PendingIntent}.
- */
- private static void linkButtons(Context context, RemoteViews views, boolean playerActive)
- {
- Intent intent = new Intent(context, NavigationActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- if (playerActive)
- intent.putExtra(Constants.INTENT_SHOW_PLAYER, true);
-
- intent.setAction("android.intent.action.MAIN");
- intent.addCategory("android.intent.category.LAUNCHER");
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 10, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- views.setOnClickPendingIntent(R.id.appwidget_coverart, pendingIntent);
- views.setOnClickPendingIntent(R.id.appwidget_top, pendingIntent);
-
- // Emulate media button clicks.
- intent = new Intent(Constants.CMD_PROCESS_KEYCODE);
- intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class));
- intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
- pendingIntent = PendingIntent.getBroadcast(context, 11, intent, 0);
- views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
-
- intent = new Intent(Constants.CMD_PROCESS_KEYCODE);
- intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class));
- intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT));
- pendingIntent = PendingIntent.getBroadcast(context, 12, intent, 0);
- views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
-
- intent = new Intent(Constants.CMD_PROCESS_KEYCODE);
- intent.setComponent(new ComponentName(context, MediaButtonIntentReceiver.class));
- intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
- pendingIntent = PendingIntent.getBroadcast(context, 13, intent, 0);
- views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.java
deleted file mode 100644
index 0c8a8ca8..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- This file is part of Subsonic.
-
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see .
-
- Copyright 2010 (C) Sindre Mehus
- */
-package org.moire.ultrasonic.provider;
-
-import org.moire.ultrasonic.R;
-
-public class UltrasonicAppWidgetProvider4X1 extends UltrasonicAppWidgetProvider
-{
-
- public UltrasonicAppWidgetProvider4X1()
- {
- super();
- this.layoutId = R.layout.appwidget4x1;
- }
-
- private static UltrasonicAppWidgetProvider4X1 instance;
-
- public static synchronized UltrasonicAppWidgetProvider4X1 getInstance()
- {
- if (instance == null)
- {
- instance = new UltrasonicAppWidgetProvider4X1();
- }
- return instance;
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.java
deleted file mode 100644
index 3ba12ae6..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- This file is part of Subsonic.
-
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see .
-
- Copyright 2010 (C) Sindre Mehus
- */
-package org.moire.ultrasonic.provider;
-
-import org.moire.ultrasonic.R;
-
-public class UltrasonicAppWidgetProvider4X2 extends UltrasonicAppWidgetProvider
-{
-
- public UltrasonicAppWidgetProvider4X2()
- {
- super();
- this.layoutId = R.layout.appwidget4x2;
- }
-
- private static UltrasonicAppWidgetProvider4X2 instance;
-
- public static synchronized UltrasonicAppWidgetProvider4X2 getInstance()
- {
- if (instance == null)
- {
- instance = new UltrasonicAppWidgetProvider4X2();
- }
- return instance;
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.java
deleted file mode 100644
index 15b2a561..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- This file is part of Subsonic.
-
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see .
-
- Copyright 2010 (C) Sindre Mehus
- */
-package org.moire.ultrasonic.provider;
-
-import org.moire.ultrasonic.R;
-
-public class UltrasonicAppWidgetProvider4X3 extends UltrasonicAppWidgetProvider
-{
-
- public UltrasonicAppWidgetProvider4X3()
- {
- super();
- this.layoutId = R.layout.appwidget4x3;
- }
-
- private static UltrasonicAppWidgetProvider4X3 instance;
-
- public static synchronized UltrasonicAppWidgetProvider4X3 getInstance()
- {
- if (instance == null)
- {
- instance = new UltrasonicAppWidgetProvider4X3();
- }
- return instance;
- }
-}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.java
deleted file mode 100644
index c28c55ab..00000000
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- This file is part of Subsonic.
-
- Subsonic is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Subsonic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Subsonic. If not, see .
-
- Copyright 2010 (C) Sindre Mehus
- */
-package org.moire.ultrasonic.provider;
-
-import org.moire.ultrasonic.R;
-
-public class UltrasonicAppWidgetProvider4X4 extends UltrasonicAppWidgetProvider
-{
-
- public UltrasonicAppWidgetProvider4X4()
- {
- super();
- this.layoutId = R.layout.appwidget4x4;
- }
-
- private static UltrasonicAppWidgetProvider4X4 instance;
-
- public static synchronized UltrasonicAppWidgetProvider4X4 getInstance()
- {
- if (instance == null)
- {
- instance = new UltrasonicAppWidgetProvider4X4();
- }
- return instance;
- }
-}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt
new file mode 100644
index 00000000..46acf216
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.kt
@@ -0,0 +1,207 @@
+/*
+ * UltrasonicAppWidgetProvider.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProvider
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.Environment
+import android.view.KeyEvent
+import android.widget.RemoteViews
+import org.moire.ultrasonic.R
+import org.moire.ultrasonic.activity.NavigationActivity
+import org.moire.ultrasonic.domain.Track
+import org.moire.ultrasonic.imageloader.BitmapUtils
+import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver
+import org.moire.ultrasonic.util.Constants
+import timber.log.Timber
+import java.lang.Exception
+
+/**
+ * Widget Provider for the Ultrasonic Widgets
+ */
+open class UltrasonicAppWidgetProvider : AppWidgetProvider() {
+ @JvmField
+ protected var layoutId = 0
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray
+ ) {
+ defaultAppWidget(context, appWidgetIds)
+ }
+
+ /**
+ * Initialize given widgets to default state, where we launch Ultrasonic on default click
+ * and hide actions if service not running.
+ */
+ private fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
+ val res = context.resources
+ val views = RemoteViews(context.packageName, layoutId)
+ views.setTextViewText(R.id.title, null)
+ views.setTextViewText(R.id.album, null)
+ views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text))
+ linkButtons(context, views, false)
+ pushUpdate(context, appWidgetIds, views)
+ }
+
+ private fun pushUpdate(context: Context, appWidgetIds: IntArray?, views: RemoteViews) {
+ // Update specific list of appWidgetIds if given, otherwise default to all
+ val manager = AppWidgetManager.getInstance(context)
+ if (manager != null) {
+ if (appWidgetIds != null) {
+ manager.updateAppWidget(appWidgetIds, views)
+ } else {
+ manager.updateAppWidget(ComponentName(context, this.javaClass), views)
+ }
+ }
+ }
+
+ /**
+ * Handle a change notification coming over from [MediaPlayerController]
+ */
+ fun notifyChange(context: Context, currentSong: Track?, playing: Boolean, setAlbum: Boolean) {
+ if (hasInstances(context)) {
+ performUpdate(context, currentSong, playing, setAlbum)
+ }
+ }
+
+ /**
+ * Check against [AppWidgetManager] if there are any instances of this widget.
+ */
+ private fun hasInstances(context: Context): Boolean {
+ val manager = AppWidgetManager.getInstance(context)
+ if (manager != null) {
+ val appWidgetIds = manager.getAppWidgetIds(ComponentName(context, javaClass))
+ return appWidgetIds.isNotEmpty()
+ }
+ return false
+ }
+
+ /**
+ * Update all active widget instances by pushing changes
+ */
+ private fun performUpdate(
+ context: Context,
+ currentSong: Track?,
+ playing: Boolean,
+ setAlbum: Boolean
+ ) {
+ val res = context.resources
+ val views = RemoteViews(context.packageName, layoutId)
+ val title = currentSong?.title
+ val artist = currentSong?.artist
+ val album = currentSong?.album
+ var errorState: CharSequence? = null
+
+ // Show error message?
+ val status = Environment.getExternalStorageState()
+ if (status == Environment.MEDIA_SHARED || status == Environment.MEDIA_UNMOUNTED) {
+ errorState = res.getText(R.string.widget_sdcard_busy)
+ } else if (status == Environment.MEDIA_REMOVED) {
+ errorState = res.getText(R.string.widget_sdcard_missing)
+ } else if (currentSong == null) {
+ errorState = res.getText(R.string.widget_initial_text)
+ }
+ if (errorState != null) {
+ // Show error state to user
+ views.setTextViewText(R.id.title, null)
+ views.setTextViewText(R.id.artist, errorState)
+ if (setAlbum) {
+ views.setTextViewText(R.id.album, null)
+ }
+ views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
+ } else {
+ // No error, so show normal titles
+ views.setTextViewText(R.id.title, title)
+ views.setTextViewText(R.id.artist, artist)
+ if (setAlbum) {
+ views.setTextViewText(R.id.album, album)
+ }
+ }
+
+ // Set correct drawable for pause state
+ if (playing) {
+ views.setImageViewResource(R.id.control_play, R.drawable.media_pause_normal_dark)
+ } else {
+ views.setImageViewResource(R.id.control_play, R.drawable.media_start_normal_dark)
+ }
+
+ // Set the cover art
+ try {
+ val bitmap =
+ if (currentSong == null) null else BitmapUtils.getAlbumArtBitmapFromDisk(
+ currentSong,
+ 240
+ )
+ if (bitmap == null) {
+ // Set default cover art
+ views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
+ } else {
+ views.setImageViewBitmap(R.id.appwidget_coverart, bitmap)
+ }
+ } catch (x: Exception) {
+ Timber.e(x, "Failed to load cover art")
+ views.setImageViewResource(R.id.appwidget_coverart, R.drawable.unknown_album)
+ }
+
+ // Link actions buttons to intents
+ linkButtons(context, views, currentSong != null)
+ pushUpdate(context, null, views)
+ }
+
+ companion object {
+ /**
+ * Link up various button actions using [PendingIntent].
+ */
+ @SuppressLint("UnspecifiedImmutableFlag")
+ private fun linkButtons(context: Context, views: RemoteViews, playerActive: Boolean) {
+ var intent = Intent(
+ context,
+ NavigationActivity::class.java
+ ).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ if (playerActive) intent.putExtra(Constants.INTENT_SHOW_PLAYER, true)
+ intent.action = "android.intent.action.MAIN"
+ intent.addCategory("android.intent.category.LAUNCHER")
+ var pendingIntent =
+ PendingIntent.getActivity(context, 10, intent, PendingIntent.FLAG_UPDATE_CURRENT)
+ views.setOnClickPendingIntent(R.id.appwidget_coverart, pendingIntent)
+ views.setOnClickPendingIntent(R.id.appwidget_top, pendingIntent)
+
+ // Emulate media button clicks.
+ intent = Intent(Constants.CMD_PROCESS_KEYCODE)
+ intent.component = ComponentName(context, MediaButtonIntentReceiver::class.java)
+ intent.putExtra(
+ Intent.EXTRA_KEY_EVENT,
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)
+ )
+ pendingIntent = PendingIntent.getBroadcast(context, 11, intent, 0)
+ views.setOnClickPendingIntent(R.id.control_play, pendingIntent)
+ intent = Intent(Constants.CMD_PROCESS_KEYCODE)
+ intent.component = ComponentName(context, MediaButtonIntentReceiver::class.java)
+ intent.putExtra(
+ Intent.EXTRA_KEY_EVENT,
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)
+ )
+ pendingIntent = PendingIntent.getBroadcast(context, 12, intent, 0)
+ views.setOnClickPendingIntent(R.id.control_next, pendingIntent)
+ intent = Intent(Constants.CMD_PROCESS_KEYCODE)
+ intent.component = ComponentName(context, MediaButtonIntentReceiver::class.java)
+ intent.putExtra(
+ Intent.EXTRA_KEY_EVENT,
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PREVIOUS)
+ )
+ pendingIntent = PendingIntent.getBroadcast(context, 13, intent, 0)
+ views.setOnClickPendingIntent(R.id.control_previous, pendingIntent)
+ }
+ }
+}
\ No newline at end of file
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt
new file mode 100644
index 00000000..97c96027
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X1.kt
@@ -0,0 +1,28 @@
+/*
+ * UltrasonicAppWidgetProvider4X1.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.provider
+
+import org.moire.ultrasonic.R
+
+class UltrasonicAppWidgetProvider4X1 : UltrasonicAppWidgetProvider() {
+ companion object {
+ @get:Synchronized
+ var instance: UltrasonicAppWidgetProvider4X1? = null
+ get() {
+ if (field == null) {
+ field = UltrasonicAppWidgetProvider4X1()
+ }
+ return field
+ }
+ private set
+ }
+
+ init {
+ layoutId = R.layout.appwidget4x1
+ }
+}
\ No newline at end of file
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt
new file mode 100644
index 00000000..f30c6efe
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X2.kt
@@ -0,0 +1,28 @@
+/*
+ * UltrasonicAppWidgetProvider4X2.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.provider
+
+import org.moire.ultrasonic.R
+
+class UltrasonicAppWidgetProvider4X2 : UltrasonicAppWidgetProvider() {
+ companion object {
+ @get:Synchronized
+ var instance: UltrasonicAppWidgetProvider4X2? = null
+ get() {
+ if (field == null) {
+ field = UltrasonicAppWidgetProvider4X2()
+ }
+ return field
+ }
+ private set
+ }
+
+ init {
+ layoutId = R.layout.appwidget4x2
+ }
+}
\ No newline at end of file
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt
new file mode 100644
index 00000000..324efcee
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X3.kt
@@ -0,0 +1,28 @@
+/*
+ * UltrasonicAppWidgetProvider4X3.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.provider
+
+import org.moire.ultrasonic.R
+
+class UltrasonicAppWidgetProvider4X3 : UltrasonicAppWidgetProvider() {
+ companion object {
+ @get:Synchronized
+ var instance: UltrasonicAppWidgetProvider4X3? = null
+ get() {
+ if (field == null) {
+ field = UltrasonicAppWidgetProvider4X3()
+ }
+ return field
+ }
+ private set
+ }
+
+ init {
+ layoutId = R.layout.appwidget4x3
+ }
+}
\ No newline at end of file
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt
new file mode 100644
index 00000000..4d03970a
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider4X4.kt
@@ -0,0 +1,28 @@
+/*
+ * UltrasonicAppWidgetProvider4X4.kt
+ * Copyright (C) 2009-2022 Ultrasonic developers
+ *
+ * Distributed under terms of the GNU GPLv3 license.
+ */
+
+package org.moire.ultrasonic.provider
+
+import org.moire.ultrasonic.R
+
+class UltrasonicAppWidgetProvider4X4 : UltrasonicAppWidgetProvider() {
+ companion object {
+ @get:Synchronized
+ var instance: UltrasonicAppWidgetProvider4X4? = null
+ get() {
+ if (field == null) {
+ field = UltrasonicAppWidgetProvider4X4()
+ }
+ return field
+ }
+ private set
+ }
+
+ init {
+ layoutId = R.layout.appwidget4x4
+ }
+}
\ No newline at end of file