diff --git a/README.md b/README.md index 855681f..ec4b52e 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,20 @@ -# Projectivy Plugin : Wallpaper Provider +# Projectivy Plugin : Booru Wallpaper Provider -This is a sample project for developing a wallpaper provider plugin for Projectivy Launcher. -- /sample : sample code for the plugin service and its setting activity -- /api : api used to communicate with Projectivy through AIDL (don't change it) -- Version: 1 +This is a [Projectivy](https://xdaforums.com/t/app-android-tv-projectivy-launcher.4436549/) Plugin that allows you to set a wallpaper from a Booru imageboard. + +## Repository layout +- /booru : code for the Booru plugin service and its setting activity +- /api : api used to communicate with Projectivy through AIDL # Usage -- fork/clone the repo -- adapt the sample manifest according to your needs (at least modify the unique id) -- customize the Wallpaper provider service and Settings fragment - -# Manifest parameters -- apiVersion: api version used in your code. Projectivy will use it to detect if your plugin is compatible with its own code -- uuid: unique id (format : UUID V4) used to identify your plugin. *You must generate one* -- name: plugin name as displayed in Projectivy plugins list -- settingsActivity: class name of your settings activity. Projectivy will launch it when users click on settings button -- itemsCacheDurationMillis: how long should Projectivy keep the last wallpapers returned by this plugin before considering them expired -- updateMode: int representing the events your plugin is interested in. This should be a combination of TIME_ELAPSED, NOW_PLAYING_CHANGED, CARD_FOCUSED, PROGRAM_CARD_FOCUSED, LAUNCHER_IDLE_MODE_CHANGED (check class WallpaperUpdateEventType) - -# How it works -## Timer event -For standard wallpaper providers such as the stock Reddit wallpaper provider, the wallpapers will change according to time events. -First, Projectivy will request wallpaper(s) to the provider selected in the settings, and keep them in cache for itemsCacheDurationMillis. -Then, each X minutes (depending on user settings), Projectivy will refresh the wallpaper by fetching a random one in its cache. -When itemsCacheDurationMillis has expired, Projectivy will ask the plugin again. - -## Other events -Other events might lead to wallpaper requests if you set the corresponding updateMode (in that case, there is no cache on Projectivy side, these events are considered dynamic and only the provider knows how to react). -- card focused: each time a card is focused (used by the stock "dynamic colors" wallpaper provider) -- program card focused: each time a program card is focused (used by the stock "current focused program" wallpaper provider) -- now playing changed: each time the "now playing" metadata has changed (could be used to set wallpaper according to the music currently playing) -- launcher idle mode changed: when Projectivy enters/exits launcher idle mode - -Each of these events will lead to a call to getWallpapers() depending on your updateMode. They will also provide this function an object containing details regarding this particular event (check the Event class for more details) - -# Hints -- Be responsible : even though getWallpapers() isn't called from the UI thread, it doesn't mean you can waste precious device resources (keep in mind that many Android Tv devices have less memory or cpu power than most smartphones). -- If you're fetching wallpapers from an external source, consider using an http cache to prevent flooding it with requests. -- Don't request an update mode you won't use. This is particularly true with card focused events. Those requests might be sent each second or so when a user navigates in the launcher. -- Take advantage of the itemsCacheDurationMillis to limit requests to your plugin and leverage Projectivy's cache (the stock "Reddit" wallpaper provider uses a 12h cache, meaning you will cycle through the same wallpapers for 12 hours before they are updated) -- Don't send to many wallpapers to Projectivy: it will cache them and only use them for itemsCacheDurationMillis, so this will waste memory -- Respect authors : the wallpaper class allows you to define an author and source uri, fill them to give credit when possible +- Download the `booru-release.apk` from the releases tab and install it on your TV +- Navigate to Projectivy -> Launcher settings -> Appearance -> Wallpaper -> Launcher wallpaper +- The "Booru Wallpaper Provider" should be available. +- Configure the plugin via the "Configure" button. The following settings are available: + - Booru URL: The base URL to the Booru site, including protocol. For example: `https://danbooru.donmai.us` + - Booru Type: Only `danbooru`-based boards are currently supported. Support for more types is planned. + - Search query: The search query from which images are pulled. This is the same as you would fill in on the imageboard website's search box. Check the help pages of your booru for guidance. (i.e. the [cheatsheet](https://danbooru.donmai.us/wiki_pages/help%3Acheatsheet) for Danbooru) All tag types should work normally (including ordering, ratio, and other metatags). + - Username and API Key: These are optional. Some boorus have limitations on how many tags can be searched anonymously or by standard users. For example, Danbooru allows 2 tags for logged out and normal users, but 6 for premium accounts. Fill in these fields to authenticate. # Note -This sample is provided as is. It is by no means perfect and should serve as a quick start. +This plugin is provided as an open-source project and is distributed "as is." While the author may offer voluntary support, there is no guarantee of availability or resolution. The author is not responsible for any damages, data loss, or issues arising from the use of this plugin. Use at your own risk. diff --git a/booru/.gitignore b/booru/.gitignore new file mode 100644 index 0000000..956c004 --- /dev/null +++ b/booru/.gitignore @@ -0,0 +1,2 @@ +/build +/release \ No newline at end of file diff --git a/sample/build.gradle.kts b/booru/build.gradle.kts similarity index 81% rename from sample/build.gradle.kts rename to booru/build.gradle.kts index 4f84f47..b1f15ff 100644 --- a/sample/build.gradle.kts +++ b/booru/build.gradle.kts @@ -6,15 +6,15 @@ plugins { } android { - namespace = "tv.projectivy.plugin.wallpaperprovider.sample" + namespace = "nl.kurocon.plugin.wallpaperprovider.booru" compileSdk = 35 defaultConfig { - applicationId = "tv.projectivy.plugin.wallpaperprovider.sample" + applicationId = "nl.kurocon.plugin.wallpaperprovider.booru" minSdk = 23 targetSdk = 35 versionCode = 1 - versionName = "1.01" + versionName = "1.0" } @@ -45,5 +45,7 @@ dependencies { implementation("com.google.android.material:material:1.12.0") implementation("androidx.preference:preference-ktx:1.2.1") implementation("com.google.code.gson:gson:2.11.0") + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("org.json:json:20210307") implementation(project(":api")) } \ No newline at end of file diff --git a/sample/proguard-rules.pro b/booru/proguard-rules.pro similarity index 100% rename from sample/proguard-rules.pro rename to booru/proguard-rules.pro diff --git a/sample/src/main/AndroidManifest.xml b/booru/src/main/AndroidManifest.xml similarity index 85% rename from sample/src/main/AndroidManifest.xml rename to booru/src/main/AndroidManifest.xml index 7e6669f..0f71e40 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/booru/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ android:allowBackup="true" android:label="@string/plugin_name" android:supportsRtl="true" - android:icon="@mipmap/ic_launcher" - android:banner="@mipmap/ic_banner" + android:icon="@drawable/danbooru_logo_500" + android:banner="@drawable/danbooru_banner" android:theme="@style/Theme.ProjectivyWallpaperProvider"> @@ -50,8 +50,7 @@ android:name="apiVersion" android:value="1"/> - - + @@ -71,10 +70,10 @@ android:name="itemsCacheDurationMillis" android:value="@integer/items_cache_duration_millis"/> - + + android:value="1"/> diff --git a/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/BooruAPI.kt b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/BooruAPI.kt new file mode 100644 index 0000000..45031b5 --- /dev/null +++ b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/BooruAPI.kt @@ -0,0 +1,75 @@ +package nl.kurocon.plugin.wallpaperprovider.booru + +import android.content.Context +import okhttp3.OkHttpClient +import okhttp3.Request +import org.json.JSONArray +import org.json.JSONObject +import java.io.IOException +import java.net.URLEncoder + +class BooruAPI(context: Context) { + private val client = OkHttpClient() + + init { + PreferencesManager.init(context) + } + + @Throws(IOException::class) + fun getImageUrls(limit: Int = 20): List { + return when (PreferencesManager.booruType) { + "danbooru" -> getDanbooruImageUrls(limit = limit) + else -> getDanbooruImageUrls(limit = limit) + } + } + + @Throws(IOException::class) + fun getDanbooruImageUrls(limit: Int = 20): List { + val tags = PreferencesManager.booruTagSearch + val encodedTags = URLEncoder.encode(tags, "UTF-8") + val url = "${PreferencesManager.booruUrl}/posts.json?tags=$encodedTags&limit=$limit" + var requestBuilder = Request.Builder() + .url(url) + if (PreferencesManager.booruUserId.isNotEmpty() && PreferencesManager.booruApiKey.isNotEmpty()) { + requestBuilder = requestBuilder.addHeader("Authorization", okhttp3.Credentials.basic(PreferencesManager.booruUserId, PreferencesManager.booruApiKey)) + } + val request = requestBuilder.build() + + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + // Try to parse error message from body + val responseBody = response.body?.string() ?: throw IOException("Unexpected code ${response.code}, and response body is empty") + val jsonBody = JSONObject(responseBody) + val errorMessage = jsonBody.optString("message") + if (errorMessage.isNotEmpty()) { + throw IOException("Error ${response.code}. $errorMessage") + } + throw IOException("Unexpected code ${response.code} - ${response.body}") + } + + val responseBody = response.body?.string() ?: throw IOException("Response body is null") + val jsonArray = JSONArray(responseBody) + + // Parse JSON response into BooruImage instances + val images = mutableListOf() + for (i in 0 until jsonArray.length()) { + val jsonObject = jsonArray.getJSONObject(i) + val imageUrl = jsonObject.optString("file_url") + val title = jsonObject.optString("tag_string") + val author = jsonObject.optString("tag_string_artist") + val sourceUri = jsonObject.optString("source") + if (imageUrl.isNotEmpty()) { + images.add(BooruImage(imageUrl, title, author, sourceUri)) + } + } + return images + } + } +} + +class BooruImage( + var imageUrl: String, + var title: String, + var author: String, + var sourceUri: String +) {} diff --git a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/PreferencesManager.kt b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/PreferencesManager.kt similarity index 72% rename from sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/PreferencesManager.kt rename to booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/PreferencesManager.kt index 210d966..d83175c 100644 --- a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/PreferencesManager.kt +++ b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/PreferencesManager.kt @@ -1,4 +1,4 @@ -package tv.projectivy.plugin.wallpaperprovider.sample +package nl.kurocon.plugin.wallpaperprovider.booru import android.content.Context import android.content.SharedPreferences @@ -9,7 +9,11 @@ import com.google.gson.ToNumberPolicy import com.google.gson.reflect.TypeToken object PreferencesManager { - private const val IMAGE_URL_KEY = "image_url_key" + private const val BOORU_URL_KEY = "booru_url_key" + private const val BOORU_TYPE_KEY = "booru_type_key" + private const val BOORU_TAG_SEARCH_KEY = "booru_tag_search_key" + private const val BOORU_USER_ID_KEY = "booru_user_id_key" + private const val BOORU_API_KEY_KEY = "booru_api_key_key" lateinit var preferences: SharedPreferences @@ -46,9 +50,21 @@ object PreferencesManager { else -> throw UnsupportedOperationException("Not yet implemented") } - var imageUrl: String - get() = PreferencesManager[IMAGE_URL_KEY, "https://images.pexels.com/photos/462162/pexels-photo-462162.jpeg"] - set(value) { PreferencesManager[IMAGE_URL_KEY]=value } + var booruUrl: String + get () = PreferencesManager[BOORU_URL_KEY, "https://danbooru.donmai.us"] + set(value) { PreferencesManager[BOORU_URL_KEY] = value } + var booruType: String + get () = PreferencesManager[BOORU_TYPE_KEY, "danbooru"] + set(value) { PreferencesManager[BOORU_TYPE_KEY] = value } + var booruTagSearch: String + get () = PreferencesManager[BOORU_TAG_SEARCH_KEY, "ratio:16:9 rating:general order:random"] + set(value) { PreferencesManager[BOORU_TAG_SEARCH_KEY] = value } + var booruUserId: String + get () = PreferencesManager[BOORU_USER_ID_KEY, ""] + set(value) { PreferencesManager[BOORU_USER_ID_KEY] = value } + var booruApiKey: String + get () = PreferencesManager[BOORU_API_KEY_KEY, ""] + set(value) { PreferencesManager[BOORU_API_KEY_KEY] = value } fun export(): String { return Gson().toJson(preferences.all) diff --git a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsActivity.kt b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsActivity.kt similarity index 90% rename from sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsActivity.kt rename to booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsActivity.kt index 0e762b3..623e8ce 100644 --- a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsActivity.kt +++ b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsActivity.kt @@ -1,4 +1,4 @@ -package tv.projectivy.plugin.wallpaperprovider.sample +package nl.kurocon.plugin.wallpaperprovider.booru import android.os.Bundle import androidx.fragment.app.FragmentActivity diff --git a/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsFragment.kt b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsFragment.kt new file mode 100644 index 0000000..9dbfee2 --- /dev/null +++ b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/SettingsFragment.kt @@ -0,0 +1,155 @@ +package nl.kurocon.plugin.wallpaperprovider.booru + + +import android.os.Bundle +import androidx.appcompat.content.res.AppCompatResources +import androidx.leanback.app.GuidedStepSupportFragment +import androidx.leanback.widget.GuidanceStylist.Guidance +import androidx.leanback.widget.GuidedAction +import kotlin.CharSequence + +class SettingsFragment : GuidedStepSupportFragment() { + override fun onCreateGuidance(savedInstanceState: Bundle?): Guidance { + return Guidance( + getString(R.string.plugin_name), + getString(R.string.plugin_description), + getString(R.string.settings), + AppCompatResources.getDrawable(requireActivity(), R.drawable.danbooru_logo_500) + ) + } + + override fun onCreateActions(actions: MutableList, savedInstanceState: Bundle?) { + PreferencesManager.init(requireContext()) + + // Booru URL setting + val currentBooruUrl = PreferencesManager.booruUrl + val actionBooruUrl = GuidedAction.Builder(context) + .id(ACTION_ID_BOORU_URL) + .title(R.string.setting_booru_url_title) + .description(currentBooruUrl) + .editDescription(currentBooruUrl) + .descriptionEditable(true) + .build() + actions.add(actionBooruUrl) + + // Booru Type setting (choice menu with only Danbooru currently) + val booruTypeSubActions: MutableList = mutableListOf(); + val typeDanbooruSubAction = GuidedAction.Builder(context) + .id(SUBACTION_ID_BOORU_TYPE_DANBOORU) + .title(R.string.setting_booru_type_subtype_danbooru_title) + .description(R.string.setting_booru_type_subtype_danbooru_description) + .build() + booruTypeSubActions.add(typeDanbooruSubAction) + + val currentBooruType = PreferencesManager.booruType + val actionBooruType = GuidedAction.Builder(context) + .id(ACTION_ID_BOORU_TYPE) + .title(R.string.setting_booru_type_title) + .description(currentBooruType) + .subActions(booruTypeSubActions) + .build() + actions.add(actionBooruType) + + // Booru search tags setting + val currentBooruTagSearch = PreferencesManager.booruTagSearch + val actionBooruTagSearch = GuidedAction.Builder(context) + .id(ACTION_ID_BOORU_TAG_SEARCH) + .title(R.string.setting_booru_tag_search_title) + .description(currentBooruTagSearch) + .editDescription(currentBooruTagSearch) + .descriptionEditable(true) + .build() + actions.add(actionBooruTagSearch) + + // Divider + actions.add(GuidedAction.Builder(context) + .title("") + .description("") + .focusable(false) + .build() + ) + + // User settings information box + actions.add(GuidedAction.Builder(context) + .id(ACTION_ID_USER_SETTINGS_LABEL) + .title(R.string.setting_users_title) + .description(R.string.setting_users_description) + .infoOnly(true) + .focusable(false) + .build() + ) + + // Booru username setting + val currentBooruUserId = PreferencesManager.booruUserId + val actionBooruUserId = GuidedAction.Builder(context) + .id(ACTION_ID_BOORU_USER_ID) + .title(R.string.setting_booru_user_id_title) + .description(currentBooruUserId) + .editDescription(currentBooruUserId) + .descriptionEditable(true) + .build() + actions.add(actionBooruUserId) + + // Booru API key setting + val currentBooruApiKey = PreferencesManager.booruApiKey + val actionBooruApiKey = GuidedAction.Builder(context) + .id(ACTION_ID_BOORU_API_KEY) + .title(R.string.setting_booru_api_key_title) + .description(currentBooruApiKey) + .editDescription(currentBooruApiKey) + .descriptionEditable(true) + .build() + actions.add(actionBooruApiKey) + } + + override fun onSubGuidedActionClicked(action: GuidedAction): Boolean { + when (action.id) { + SUBACTION_ID_BOORU_TYPE_DANBOORU -> { + findActionById(ACTION_ID_BOORU_TYPE)?.description = "danbooru" + notifyActionChanged(findActionPositionById(ACTION_ID_BOORU_TYPE)) + PreferencesManager.booruType = "danbooru" + } + } + return true + } + + override fun onGuidedActionClicked(action: GuidedAction) { + when (action.id) { + ACTION_ID_BOORU_URL -> { + val params: CharSequence? = action.editDescription + findActionById(ACTION_ID_BOORU_URL)?.description = params + notifyActionChanged(findActionPositionById(ACTION_ID_BOORU_URL)) + PreferencesManager.booruUrl = params.toString() + } + ACTION_ID_BOORU_TAG_SEARCH -> { + val params: CharSequence? = action.editDescription + findActionById(ACTION_ID_BOORU_TAG_SEARCH)?.description = params + notifyActionChanged(findActionPositionById(ACTION_ID_BOORU_TAG_SEARCH)) + PreferencesManager.booruTagSearch = params.toString() + } + ACTION_ID_BOORU_USER_ID -> { + val params: CharSequence? = action.editDescription + findActionById(ACTION_ID_BOORU_USER_ID)?.description = params + notifyActionChanged(findActionPositionById(ACTION_ID_BOORU_USER_ID)) + PreferencesManager.booruUserId = params.toString() + } + ACTION_ID_BOORU_API_KEY -> { + val params: CharSequence? = action.editDescription + findActionById(ACTION_ID_BOORU_API_KEY)?.description = params + notifyActionChanged(findActionPositionById(ACTION_ID_BOORU_API_KEY)) + PreferencesManager.booruApiKey = params.toString() + } + } + } + + companion object { + private const val ACTION_ID_BOORU_URL = 1L + private const val ACTION_ID_BOORU_TYPE = 2L + private const val ACTION_ID_BOORU_TAG_SEARCH = 3L + private const val ACTION_ID_USER_SETTINGS_LABEL = 4L + private const val ACTION_ID_BOORU_USER_ID = 5L + private const val ACTION_ID_BOORU_API_KEY = 6L + + private const val SUBACTION_ID_BOORU_TYPE_DANBOORU = 7L + } +} diff --git a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/WallpaperProviderService.kt b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/WallpaperProviderService.kt similarity index 68% rename from sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/WallpaperProviderService.kt rename to booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/WallpaperProviderService.kt index 2d1877e..5ef3017 100644 --- a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/WallpaperProviderService.kt +++ b/booru/src/main/java/nl/kurocon/plugin/wallpaperprovider/booru/WallpaperProviderService.kt @@ -1,14 +1,20 @@ -package tv.projectivy.plugin.wallpaperprovider.sample +package nl.kurocon.plugin.wallpaperprovider.booru import android.app.Service import android.content.ContentResolver import android.content.Intent import android.net.Uri import android.os.IBinder +import android.widget.Toast +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import tv.projectivy.plugin.wallpaperprovider.api.Event import tv.projectivy.plugin.wallpaperprovider.api.IWallpaperProviderService import tv.projectivy.plugin.wallpaperprovider.api.Wallpaper import tv.projectivy.plugin.wallpaperprovider.api.WallpaperType +import java.io.IOException class WallpaperProviderService: Service() { @@ -22,6 +28,32 @@ class WallpaperProviderService: Service() { return binder } + private fun getNewWallpapers(): List { + try { + return BooruAPI(this).getImageUrls(20).map { + Wallpaper( + uri = it.imageUrl, + type = WallpaperType.IMAGE, + author = it.author, + actionUri = it.sourceUri, + title = it.title, + source = it.author + ) + } + } catch (e: IOException) { + CoroutineScope(Dispatchers.IO).launch { + withContext(Dispatchers.Main) { + Toast.makeText( + this@WallpaperProviderService, + "Failed to fetch new images: ${e.message}", + Toast.LENGTH_LONG + ).show() + } + } + return emptyList() + } + } + private val binder = object : IWallpaperProviderService.Stub() { override fun getWallpapers(event: Event?): List { @@ -29,18 +61,7 @@ class WallpaperProviderService: Service() { return when (event) { is Event.TimeElapsed -> { // This is where you generate the wallpaper list that will be cycled every x minute - return listOf( - // DRAWABLE can be served from app drawable/ - Wallpaper(getDrawableUri(R.drawable.ic_banner_drawable).toString(), WallpaperType.DRAWABLE), - // IMAGE can be served from app drawable/, local storage or internet - Wallpaper(PreferencesManager.imageUrl, WallpaperType.IMAGE, author = "Pixabay"), - // ANIMATED_DRAWABLE can be served from app drawable/ - Wallpaper(getDrawableUri(R.drawable.anim_sample).toString(), WallpaperType.ANIMATED_DRAWABLE), - // LOTTIE can be served from app raw/, local storage or internet - Wallpaper(getDrawableUri(R.raw.gradient).toString(), WallpaperType.LOTTIE), - // VIDEO can be served from app raw/, local storage or internet (some formats might not be supported, though) - Wallpaper(getDrawableUri(R.raw.light).toString(), WallpaperType.VIDEO) - ) + return getNewWallpapers() } // Below are "dynamic events" that might interest you in special cases @@ -57,7 +78,7 @@ class WallpaperProviderService: Service() { is Event.ProgramCardFocused -> emptyList() // When Projectivy enters or exits idle mode is Event.LauncherIdleModeChanged -> { - return if (event.isIdle) { listOf(Wallpaper(getDrawableUri(R.drawable.ic_plugin).toString(), WallpaperType.DRAWABLE)) } + return if (event.isIdle) { getNewWallpapers() } else emptyList() } else -> emptyList() // Returning an empty list won't change the currently displayed wallpaper diff --git a/booru/src/main/res/drawable/danbooru_banner.xml b/booru/src/main/res/drawable/danbooru_banner.xml new file mode 100644 index 0000000..df1f233 --- /dev/null +++ b/booru/src/main/res/drawable/danbooru_banner.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/booru/src/main/res/drawable/danbooru_logo_500.png b/booru/src/main/res/drawable/danbooru_logo_500.png new file mode 100644 index 0000000..5e652ad Binary files /dev/null and b/booru/src/main/res/drawable/danbooru_logo_500.png differ diff --git a/booru/src/main/res/raw/danbooru_banner.svg b/booru/src/main/res/raw/danbooru_banner.svg new file mode 100644 index 0000000..001c31e --- /dev/null +++ b/booru/src/main/res/raw/danbooru_banner.svg @@ -0,0 +1,404 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/src/main/res/values/colors.xml b/booru/src/main/res/values/colors.xml similarity index 100% rename from sample/src/main/res/values/colors.xml rename to booru/src/main/res/values/colors.xml diff --git a/booru/src/main/res/values/strings.xml b/booru/src/main/res/values/strings.xml new file mode 100644 index 0000000..76ef1e5 --- /dev/null +++ b/booru/src/main/res/values/strings.xml @@ -0,0 +1,24 @@ + + Booru Wallpaper Provider + + A wallpaper provider that can talk to Booru sites running Danbooru software. + + 893434d0-4901-4ad9-a366-50f93ea2f285 + + + Settings + Image URL + Booru URL - e.g. \'https://danbooru.donmai.us/\' + Booru Type + Search query - e.g. \'ratio:16:9 rating:general\' + Booru Username + Booru API Key + + User settings (optional) + + Logging in is optional, but if you have a premium account you can use more tags in your search. + + + Danbooru + Danbooru-based (danbooru.donmai.us) - Currently the only supported type + diff --git a/sample/src/main/res/values/themes.xml b/booru/src/main/res/values/themes.xml similarity index 100% rename from sample/src/main/res/values/themes.xml rename to booru/src/main/res/values/themes.xml diff --git a/sample/src/main/res/values/values.xml b/booru/src/main/res/values/values.xml similarity index 100% rename from sample/src/main/res/values/values.xml rename to booru/src/main/res/values/values.xml diff --git a/sample/.gitignore b/sample/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/sample/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsFragment.kt b/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsFragment.kt deleted file mode 100644 index 1b68792..0000000 --- a/sample/src/main/java/tv/projectivy/plugin/wallpaperprovider/sample/SettingsFragment.kt +++ /dev/null @@ -1,50 +0,0 @@ -package tv.projectivy.plugin.wallpaperprovider.sample - - -import android.os.Bundle -import android.util.Log -import androidx.appcompat.content.res.AppCompatResources -import androidx.leanback.app.GuidedStepSupportFragment -import androidx.leanback.widget.GuidanceStylist.Guidance -import androidx.leanback.widget.GuidedAction -import kotlin.CharSequence - -class SettingsFragment : GuidedStepSupportFragment() { - override fun onCreateGuidance(savedInstanceState: Bundle?): Guidance { - return Guidance( - getString(R.string.plugin_name), - getString(R.string.plugin_description), - getString(R.string.settings), - AppCompatResources.getDrawable(requireActivity(), R.drawable.ic_plugin) - ) - } - - override fun onCreateActions(actions: MutableList, savedInstanceState: Bundle?) { - PreferencesManager.init(requireContext()) - - val currentImageUrl = PreferencesManager.imageUrl - val action = GuidedAction.Builder(context) - .id(ACTION_ID_IMAGE_URL) - .title(R.string.setting_image_url_title) - .description(currentImageUrl) - .editDescription(currentImageUrl) - .descriptionEditable(true) - .build() - actions.add(action) - } - - override fun onGuidedActionClicked(action: GuidedAction) { - when (action.id) { - ACTION_ID_IMAGE_URL -> { - val params: CharSequence? = action.editDescription - findActionById(ACTION_ID_IMAGE_URL)?.description = params - notifyActionChanged(findActionPositionById(ACTION_ID_IMAGE_URL)) - PreferencesManager.imageUrl = params.toString() - } - } - } - - companion object { - private const val ACTION_ID_IMAGE_URL = 1L - } -} diff --git a/sample/src/main/res/drawable/anim_sample.xml b/sample/src/main/res/drawable/anim_sample.xml deleted file mode 100644 index 52cf441..0000000 --- a/sample/src/main/res/drawable/anim_sample.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample/src/main/res/drawable/ic_banner_drawable.xml b/sample/src/main/res/drawable/ic_banner_drawable.xml deleted file mode 100644 index 39c1886..0000000 --- a/sample/src/main/res/drawable/ic_banner_drawable.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/sample/src/main/res/drawable/ic_launcher_foreground.xml b/sample/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index fcda109..0000000 --- a/sample/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/sample/src/main/res/drawable/ic_plugin.xml b/sample/src/main/res/drawable/ic_plugin.xml deleted file mode 100644 index 9db92d6..0000000 --- a/sample/src/main/res/drawable/ic_plugin.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_banner.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_banner.xml deleted file mode 100644 index 04d6af6..0000000 --- a/sample/src/main/res/mipmap-anydpi-v26/ic_banner.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 2955b61..0000000 --- a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/sample/src/main/res/mipmap-xhdpi/ic_banner.webp b/sample/src/main/res/mipmap-xhdpi/ic_banner.webp deleted file mode 100644 index 053aacd..0000000 Binary files a/sample/src/main/res/mipmap-xhdpi/ic_banner.webp and /dev/null differ diff --git a/sample/src/main/res/raw/gradient.json b/sample/src/main/res/raw/gradient.json deleted file mode 100644 index e1fe681..0000000 --- a/sample/src/main/res/raw/gradient.json +++ /dev/null @@ -1 +0,0 @@ -{"v":"5.4.4","fr":29.9700012207031,"ip":0,"op":120.0000048877,"w":1920,"h":1080,"nm":"Gradient 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0],"e":[360]},{"t":119.000004846969}],"ix":10},"p":{"a":0,"k":[960,540,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":2,"s":{"a":0,"k":[2489,1768.076],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,0.49,0.373,0.5,0.998,0.575,0.416,1,0.996,0.659,0.459],"ix":9}},"s":{"a":0,"k":[-411.974,-6.334],"ix":5},"e":{"a":0,"k":[352.025,-5.646],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-68,-34],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,184.116],"ix":3},"r":{"a":0,"k":89.879,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120.0000048877,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/sample/src/main/res/raw/light.mp4 b/sample/src/main/res/raw/light.mp4 deleted file mode 100644 index 4bf53bc..0000000 Binary files a/sample/src/main/res/raw/light.mp4 and /dev/null differ diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml deleted file mode 100644 index 5fdf4d4..0000000 --- a/sample/src/main/res/values/strings.xml +++ /dev/null @@ -1,10 +0,0 @@ - - Projectivy Wallpaper Provider Sample - Plugin description - CHANGE_ME - - - Settings - Image URL - - \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 0a615ad..4fdc45f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,6 @@ dependencyResolutionManagement { } } -rootProject.name = "Projectivy Wallpaper Provider" -include(":sample") +rootProject.name = "Projectivy Booru Wallpaper Provider" +include(":booru") include(":api")