Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(plugin): introducing plugin groups #255

Merged
merged 13 commits into from
Aug 12, 2023
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# Android Pluto
<a href="https://www.producthunt.com/posts/pluto-0265aff1-a022-48a7-bea2-85d33cf1f15a?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-pluto&#0045;0265aff1&#0045;a022&#0045;48a7&#0045;bea2&#0045;85d33cf1f15a" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=349545&theme=light" alt="Pluto - Open&#0045;sourced&#0032;On&#0045;device&#0032;debug&#0032;framework&#0032;for&#0032;Android&#0032;apps | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>

[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.plutolib/pluto/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.plutolib/pluto)
[![CLA assistant](https://cla-assistant.io/readme/badge/androidPluto/pluto)](https://cla-assistant.io/androidPluto/pluto)
Expand Down Expand Up @@ -77,6 +76,34 @@ Now re-build and run your app, you will receive a notification from Pluto, use i

<br>

## Grouping Plugins *(Optional)*
Pluto now allows to group similar plugins together to have better readability & categorization.
<br>
To create a group, we need to override PluginGroup & attach Plugins to it. *(We have taken the example of grouping datasource plugins together)*

```kotlin
class DataSourcePluginGroup : PluginGroup("datasource-group") {

override fun getConfig() = PluginGroupConfiguration(
name = "DataSource Group"
)

override fun getPlugins() = listOf(
PlutoSharePreferencesPlugin(),
PlutoDatastorePreferencesPlugin(),
PlutoRoomsDatabasePlugin()
)
}
```

Then add the group to Plugin installer.
```kotlin
Pluto.Installer(this)
.addPluginGroup(DataSourcePluginGroup())
.install()
```

<br>

## 📝 &nbsp;Contribution

Expand Down
25 changes: 5 additions & 20 deletions pluto-plugins/base/lib/src/main/java/com/pluto/plugin/DataModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,9 @@ data class PluginConfiguration(
val name: String,
@DrawableRes val icon: Int = R.drawable.pluto___ic_plugin_placeholder_icon,
val version: String
) : ListItem() {
val identifier = name.lowercase().replace(" ", "_", true)
) : ListItem()

override fun equals(other: Any?): Boolean {
return other is PluginConfiguration && identifier == other.identifier
}

override fun hashCode(): Int {
return identifier.hashCode()
}
}

data class PluginOption(
val id: String,
val label: String,
@DrawableRes val icon: Int = R.drawable.pluto___ic_plugin_placeholder_icon
) : ListItem() {
override fun equals(other: Any?): Boolean {
return other is PluginOption && id == other.id
}
}
data class PluginGroupConfiguration(
val name: String,
@DrawableRes val icon: Int = R.drawable.pluto___ic_plugin_group_placeholder_icon,
) : ListItem()
12 changes: 2 additions & 10 deletions pluto-plugins/base/lib/src/main/java/com/pluto/plugin/Plugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import androidx.annotation.Keep
import androidx.fragment.app.Fragment
import com.pluto.utilities.DebugLog
import com.pluto.utilities.extensions.toast
import com.pluto.utilities.list.ListItem

@Keep
abstract class Plugin(val devIdentifier: String) : ListItem() {
abstract class Plugin(val identifier: String) : PluginEntity(identifier) {

val context: Context
get() = returnContext()
Expand All @@ -35,15 +34,14 @@ abstract class Plugin(val devIdentifier: String) : ListItem() {
var savedInstance: Bundle = Bundle()
private set

fun install(application: Application) {
final override fun install(application: Application) {
this._application = application
onPluginInstalled()
}

abstract fun getConfig(): PluginConfiguration
abstract fun getView(): Fragment
open fun getDeveloperDetails(): DeveloperDetails? = null
open fun shouldInstallPlugin(): Boolean = true

/**
* plugin lifecycle methods
Expand All @@ -55,10 +53,4 @@ abstract class Plugin(val devIdentifier: String) : ListItem() {
DebugLog.d("pluto_plugin", "view switched :: ${getConfig().name} : $savedInstanceState")
context.toast("View switched to ${getConfig().name}")
}

@Deprecated("global level plugin options are no longer supported")
fun getOptions(): List<PluginOption> = emptyList()

override fun equals(other: Any?): Boolean = other is Plugin && devIdentifier == other.devIdentifier
override fun hashCode(): Int = devIdentifier.hashCode()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.pluto.plugin

import android.app.Application
import com.pluto.utilities.list.ListItem

abstract class PluginEntity(private val identifier: String) : ListItem() {

abstract fun install(application: Application)
override fun equals(other: Any?): Boolean = other is PluginEntity && identifier == other.identifier
override fun hashCode(): Int = identifier.hashCode()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
package com.pluto.plugin

import com.pluto.utilities.list.ListItem
import android.app.Application

class PluginGroup : ListItem()
abstract class PluginGroup(identifier: String) : PluginEntity(identifier) {

private var plugins: LinkedHashSet<Plugin> = linkedSetOf()

val installedPlugins: List<Plugin>
get() {
val list = arrayListOf<Plugin>()
list.addAll(plugins)
return list
}

abstract fun getConfig(): PluginGroupConfiguration

protected abstract fun getPlugins(): List<Plugin>

final override fun install(application: Application) {
getPlugins().forEach {
it.install(application)
plugins.add(it)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/pluto___dark"
android:pathData="M6,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM6,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM6,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM3,9.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM6,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM21,10.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM14,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM14,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM3,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,20.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,3.5c0.28,0 0.5,-0.22 0.5,-0.5s-0.22,-0.5 -0.5,-0.5 -0.5,0.22 -0.5,0.5 0.22,0.5 0.5,0.5zM10,7c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1 -1,0.45 -1,1 0.45,1 1,1zM10,12.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM18,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM18,5c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM21,13.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM14,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM14,20.5c-0.28,0 -0.5,0.22 -0.5,0.5s0.22,0.5 0.5,0.5 0.5,-0.22 0.5,-0.5 -0.22,-0.5 -0.5,-0.5zM10,8.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM10,17c-0.55,0 -1,0.45 -1,1s0.45,1 1,1 1,-0.45 1,-1 -0.45,-1 -1,-1zM14,12.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM14,8.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5z" />
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,20 @@ internal class DemoNotification(private val context: Context) {
private val device = Device(context)

fun add() {
Session.devIdentifier?.let {
createChannel()
val notification: Notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentTitle(context.getString(R.string.demo___notification_title, device.app.name))
.setContentText(context.getString(R.string.demo___notification_subtitle))
.setSmallIcon(R.drawable.demo___ic_plugin_icon)
.setContentIntent(PlutoInterface.notification.getPendingIntent(it))
.setOngoing(false)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.setSilent(true)
.setSound(null)
.build()
manager?.notify(NOTIFICATION_ID, notification)
}
createChannel()
val notification: Notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentTitle(context.getString(R.string.demo___notification_title, device.app.name))
.setContentText(context.getString(R.string.demo___notification_subtitle))
.setSmallIcon(R.drawable.demo___ic_plugin_icon)
.setContentIntent(PlutoInterface.notification.getPendingIntent(DemoPlugin.ID))
.setOngoing(false)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.setSilent(true)
.setSound(null)
.build()
manager?.notify(NOTIFICATION_ID, notification)
}

fun remove() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ class DemoPlugin() : Plugin(ID) {
@Deprecated("Use the default constructor DemoPlugin() instead.")
constructor(identifier: String) : this()

init {
Session.devIdentifier = devIdentifier
}

override fun getConfig(): PluginConfiguration = PluginConfiguration(
name = context.getString(R.string.demo___plugin_name),
icon = R.drawable.demo___ic_plugin_icon,
Expand All @@ -33,7 +29,7 @@ class DemoPlugin() : Plugin(ID) {
override fun getView(): Fragment = DemoFragment()

override fun onPluginInstalled() {
DebugLog.d("demo_plugin", "$devIdentifier installed")
DebugLog.d("demo_plugin", "$ID installed")
}

override fun onPluginDataCleared() {
Expand Down

This file was deleted.

22 changes: 12 additions & 10 deletions pluto/lib/src/main/java/com/pluto/Pluto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import com.pluto.core.notch.Notch
import com.pluto.core.notch.NotchStateCallback
import com.pluto.core.notification.NotificationManager
import com.pluto.plugin.Plugin
import com.pluto.plugin.PluginEntity
import com.pluto.plugin.PluginGroup
import com.pluto.plugin.PluginManager
import com.pluto.plugin.libinterface.NotificationInterface.Companion.BUNDLE_LABEL
import com.pluto.plugin.libinterface.NotificationInterface.Companion.ID_LABEL
Expand Down Expand Up @@ -38,7 +40,7 @@ object Pluto {
internal lateinit var selectorStateCallback: SelectorStateCallback
private lateinit var notchStateCallback: NotchStateCallback

private fun init(application: Application, plugins: LinkedHashSet<Plugin>) {
private fun init(application: Application, plugins: LinkedHashSet<PluginEntity>) {
initialiseCallbacks()
this.application = application
appLifecycle = AppLifecycle(appStateCallback)
Expand Down Expand Up @@ -79,14 +81,9 @@ object Pluto {
notch?.enable(state)
}

fun clearAllLogs() {
pluginManager.installedPlugins.forEach { it.onPluginDataCleared() }
}

fun clearLogs(plugin: Plugin) {
if (pluginManager.installedPlugins.contains(plugin)) {
plugin.onPluginDataCleared()
}
@JvmOverloads
fun clearLogs(identifier: String? = null) {
pluginManager.clearLogs(identifier)
}

private fun initialiseCallbacks() {
Expand All @@ -98,13 +95,18 @@ object Pluto {

class Installer(private val application: Application) {

private val plugins = linkedSetOf<Plugin>()
private val plugins = linkedSetOf<PluginEntity>()

fun addPlugin(plugin: Plugin): Installer {
plugins.add(plugin)
return this
}

fun addPluginGroup(pluginGroup: PluginGroup): Installer {
plugins.add(pluginGroup)
return this
}

fun install() {
init(application, plugins)
}
Expand Down
38 changes: 25 additions & 13 deletions pluto/lib/src/main/java/com/pluto/plugin/PluginManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import android.app.Application
import com.pluto.plugin.libinterface.PlutoInterface
import com.pluto.ui.container.PlutoActivity
import com.pluto.ui.selector.SelectorActivity
import com.pluto.utilities.DebugLog

internal class PluginManager(private val application: Application) {

private var plugins: LinkedHashSet<Plugin> = linkedSetOf()
internal val installedPlugins: List<Plugin>
private var plugins: LinkedHashSet<PluginEntity> = linkedSetOf()
internal val installedPlugins: List<PluginEntity>
get() {
val list = arrayListOf<Plugin>()
val list = arrayListOf<PluginEntity>()
list.addAll(plugins)
return list
}
Expand All @@ -24,20 +23,33 @@ internal class PluginManager(private val application: Application) {
)
}

fun install(plugins: LinkedHashSet<Plugin>) {
fun install(plugins: LinkedHashSet<PluginEntity>) {
plugins.forEach {
if (it.shouldInstallPlugin()) {
it.install(application)
this.plugins.add(it)
} else {
DebugLog.e("pluto_plugin", "${it.getConfig().name} not installed (reason: condition mismatch).")
}
it.install(application)
this.plugins.add(it)
}
}

fun get(identifier: String): Plugin? {
return plugins.firstOrNull {
it.devIdentifier == identifier
plugins.forEach {
when (it) {
is Plugin -> if (it.identifier == identifier) return it
is PluginGroup -> return it.installedPlugins.firstOrNull { plugin -> plugin.identifier == identifier }
}
}
return null
}

fun clearLogs(identifier: String? = null) {
identifier?.let { get(identifier)?.onPluginDataCleared() } ?: run { clearAllLogs() }
}

private fun clearAllLogs() {
installedPlugins.forEach {
when (it) {
is Plugin -> it.onPluginDataCleared()
is PluginGroup -> it.installedPlugins.forEach { plugin -> plugin.onPluginDataCleared() }
}
}
}
}
4 changes: 2 additions & 2 deletions pluto/lib/src/main/java/com/pluto/plugin/PluginsViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import com.pluto.Pluto

internal class PluginsViewModel(application: Application) : AndroidViewModel(application) {

val plugins: LiveData<List<Plugin>>
val plugins: LiveData<List<PluginEntity>>
get() = _plugins
private val _plugins = MutableLiveData<List<Plugin>>()
private val _plugins = MutableLiveData<List<PluginEntity>>()

init {
_plugins.postValue(Pluto.pluginManager.installedPlugins)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.pluto.plugin.selector

import android.view.ViewGroup
import com.pluto.plugin.Plugin
import com.pluto.plugin.PluginGroup
import com.pluto.utilities.list.BaseAdapter
import com.pluto.utilities.list.DiffAwareHolder
import com.pluto.utilities.list.ListItem
Expand All @@ -10,18 +11,21 @@ internal class PluginAdapter(private val listener: OnActionListener) : BaseAdapt
override fun getItemViewType(item: ListItem): Int? {
return when (item) {
is Plugin -> ITEM_TYPE_PLUGIN
is PluginGroup -> ITEM_TYPE_PLUGIN_GROUP
else -> null
}
}

override fun onViewHolderCreated(parent: ViewGroup, viewType: Int): DiffAwareHolder? {
return when (viewType) {
ITEM_TYPE_PLUGIN -> PluginItemHolder(parent, listener)
ITEM_TYPE_PLUGIN_GROUP -> PluginGroupItemHolder(parent, listener)
else -> null
}
}

companion object {
const val ITEM_TYPE_PLUGIN = 1000
const val ITEM_TYPE_PLUGIN_GROUP = 1001
}
}
Loading
Loading