Skip to content

Commit

Permalink
feature(plugin): introducing plugin groups (#255)
Browse files Browse the repository at this point in the history
* setup & cleanup

* fixes

* group item in selector

* grouped plugin bottomsheet added

* grouped plugin flow added

* grouped plugin ui

* ui fixes

* ui fixes

* group icon fixes

* refactor

* Update README.md

* Update README.md

* ui fix
  • Loading branch information
srtvprateek authored Aug 12, 2023
1 parent cf52f29 commit c52369f
Show file tree
Hide file tree
Showing 25 changed files with 510 additions and 110 deletions.
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

0 comments on commit c52369f

Please sign in to comment.