-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Basic starter to add Widget support.
- Loading branch information
1 parent
364fe2b
commit a3c4e6f
Showing
16 changed files
with
382 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
node_modules/ | ||
dist/ | ||
vendor/ | ||
cache/ | ||
.*/ | ||
*.min.* | ||
*.test.* | ||
*.spec.* | ||
*.bundle.* | ||
*.bundle-min.* | ||
*.*.js | ||
*.*.ts | ||
*.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,7 @@ android { | |
} | ||
buildFeatures { | ||
viewBinding = true | ||
dataBinding = true | ||
} | ||
|
||
applicationVariants.all { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.github.droidworksstudio.launcher.ui.widgetmanager | ||
|
||
import android.appwidget.AppWidgetHost | ||
import android.appwidget.AppWidgetManager | ||
import android.content.Context | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.FrameLayout | ||
import androidx.recyclerview.widget.RecyclerView | ||
import com.github.droidworksstudio.launcher.R | ||
|
||
class WidgetAdapter( | ||
private val context: Context, | ||
private val appWidgetHost: AppWidgetHost, | ||
private val onWidgetClick: (Int) -> Unit | ||
) : RecyclerView.Adapter<WidgetAdapter.WidgetViewHolder>() { | ||
|
||
private val widgetItems = mutableListOf<WidgetItem>() | ||
|
||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WidgetViewHolder { | ||
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_widget, parent, false) | ||
return WidgetViewHolder(itemView) | ||
} | ||
|
||
override fun onBindViewHolder(holder: WidgetViewHolder, position: Int) { | ||
val widgetItem = widgetItems[position] | ||
holder.bind(widgetItem) | ||
} | ||
|
||
override fun getItemCount(): Int = widgetItems.size | ||
|
||
fun addWidget(widgetItem: WidgetItem) { | ||
widgetItems.add(widgetItem) | ||
notifyItemInserted(widgetItems.size - 1) | ||
} | ||
|
||
fun removeWidget(appWidgetId: Int) { | ||
val position = widgetItems.indexOfFirst { it.appWidgetId == appWidgetId } | ||
if (position != -1) { | ||
widgetItems.removeAt(position) | ||
notifyItemRemoved(position) | ||
} | ||
} | ||
|
||
fun getWidgetIds(): List<Int> = widgetItems.map { it.appWidgetId } | ||
|
||
inner class WidgetViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | ||
fun bind(widgetItem: WidgetItem) { | ||
val hostView = appWidgetHost.createView(context, widgetItem.appWidgetId, widgetItem.appWidgetInfo) | ||
itemView.findViewById<FrameLayout>(R.id.widget_frame).addView(hostView) | ||
|
||
itemView.setOnClickListener { | ||
onWidgetClick(widgetItem.appWidgetId) | ||
} | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
app/src/main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetItem.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.github.droidworksstudio.launcher.ui.widgetmanager | ||
|
||
import android.appwidget.AppWidgetProviderInfo | ||
|
||
data class WidgetItem(val appWidgetId: Int, val appWidgetInfo: AppWidgetProviderInfo?) |
182 changes: 182 additions & 0 deletions
182
.../main/java/com/github/droidworksstudio/launcher/ui/widgetmanager/WidgetManagerFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package com.github.droidworksstudio.launcher.ui.widgetmanager | ||
|
||
import android.app.Activity | ||
import android.appwidget.AppWidgetHost | ||
import android.appwidget.AppWidgetManager | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.os.Bundle | ||
import android.util.Log | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.activity.result.ActivityResultLauncher | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.fragment.app.Fragment | ||
import androidx.recyclerview.widget.LinearLayoutManager | ||
import com.github.droidworksstudio.launcher.Constants | ||
import com.github.droidworksstudio.launcher.databinding.FragmentWidgetManagerBinding | ||
import com.github.droidworksstudio.launcher.helper.AppHelper | ||
import dagger.hilt.android.AndroidEntryPoint | ||
import javax.inject.Inject | ||
|
||
/** | ||
* A simple [Fragment] subclass as the second destination in the navigation. | ||
*/ | ||
@AndroidEntryPoint | ||
class WidgetManagerFragment : Fragment(), WidgetOptionsDialogFragment.WidgetOptionsListener { | ||
|
||
private var _binding: FragmentWidgetManagerBinding? = null | ||
|
||
private val binding get() = _binding!! | ||
|
||
private lateinit var pickWidgetLauncher: ActivityResultLauncher<Intent> | ||
private lateinit var createWidgetLauncher: ActivityResultLauncher<Intent> | ||
|
||
private lateinit var appWidgetManager: AppWidgetManager | ||
private lateinit var appWidgetHost: AppWidgetHost | ||
private lateinit var widgetAdapter: WidgetAdapter | ||
|
||
@Inject | ||
lateinit var appHelper: AppHelper | ||
|
||
private lateinit var context: Context | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View { | ||
|
||
_binding = FragmentWidgetManagerBinding.inflate(inflater, container, false) | ||
|
||
return binding.root | ||
|
||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
appHelper.dayNightMod(requireContext(), binding.drawBackground) | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
context = requireContext() | ||
|
||
appWidgetManager = AppWidgetManager.getInstance(requireContext()) | ||
appWidgetHost = AppWidgetHost(requireContext(), Constants.APP_WIDGET_HOST_ID) | ||
|
||
widgetAdapter = WidgetAdapter(requireContext(), appWidgetHost) { appWidgetId -> | ||
showWidgetOptionsDialog(appWidgetId) | ||
} | ||
|
||
binding.widgetContainer.apply { | ||
layoutManager = LinearLayoutManager(requireContext()) | ||
adapter = widgetAdapter | ||
} | ||
|
||
// Load saved widgets | ||
val savedWidgetIds = loadWidgetIds() | ||
for (widgetId in savedWidgetIds) { | ||
addWidget(widgetId) | ||
} | ||
|
||
// Initialize the ActivityResultLaunchers | ||
pickWidgetLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> | ||
if (result.resultCode == Activity.RESULT_OK) { | ||
val data = result.data | ||
data?.let { | ||
val appWidgetId = it.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) | ||
if (appWidgetId != -1) { | ||
val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) | ||
if (appWidgetInfo?.configure != null) { | ||
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) | ||
intent.component = appWidgetInfo.configure | ||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) | ||
createWidgetLauncher.launch(intent) | ||
} else { | ||
addWidget(appWidgetId) | ||
} | ||
} | ||
} | ||
} else if (result.resultCode == Activity.RESULT_CANCELED) { | ||
val appWidgetId = result.data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) | ||
if (appWidgetId != null && appWidgetId != -1) { | ||
appWidgetHost.deleteAppWidgetId(appWidgetId) | ||
} | ||
} | ||
} | ||
|
||
createWidgetLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> | ||
if (result.resultCode == Activity.RESULT_OK) { | ||
val data = result.data | ||
data?.let { | ||
val appWidgetId = it.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) | ||
if (appWidgetId != -1) { | ||
addWidget(appWidgetId) | ||
} | ||
} | ||
} else if (result.resultCode == Activity.RESULT_CANCELED) { | ||
val appWidgetId = result.data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) | ||
if (appWidgetId != null && appWidgetId != -1) { | ||
appWidgetHost.deleteAppWidgetId(appWidgetId) | ||
} | ||
} | ||
} | ||
|
||
// Set long-click listener on the root view | ||
binding.widgetParent.setOnLongClickListener { | ||
selectWidget() | ||
true // Indicate that the long-click event has been handled | ||
} | ||
} | ||
|
||
private fun selectWidget() { | ||
val appWidgetId = appWidgetHost.allocateAppWidgetId() | ||
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK) | ||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) | ||
pickWidgetLauncher.launch(intent) | ||
} | ||
|
||
|
||
private fun addWidget(appWidgetId: Int) { | ||
val appWidgetInfo = appWidgetManager.getAppWidgetInfo(appWidgetId) | ||
appWidgetInfo?.let { | ||
val widgetItem = WidgetItem(appWidgetId, it) | ||
widgetAdapter.addWidget(widgetItem) | ||
saveWidgetIds(widgetAdapter.getWidgetIds()) // Save the updated widget IDs | ||
} ?: Log.e("WidgetManagerFragment", "Failed to get app widget info for ID: $appWidgetId") | ||
} | ||
|
||
override fun onRemoveWidget(appWidgetId: Int) { | ||
appWidgetHost.deleteAppWidgetId(appWidgetId) | ||
widgetAdapter.removeWidget(appWidgetId) | ||
saveWidgetIds(widgetAdapter.getWidgetIds()) | ||
} | ||
|
||
private fun showWidgetOptionsDialog(appWidgetId: Int) { | ||
val dialog = WidgetOptionsDialogFragment.newInstance(appWidgetId) | ||
dialog.show(childFragmentManager, "WidgetOptionsDialog") | ||
} | ||
|
||
override fun onOpenAppWidget(appWidgetId: Int) { | ||
// Implement the logic to open the widget's configuration or app | ||
// This is typically dependent on the widget and its associated app | ||
} | ||
|
||
private fun saveWidgetIds(widgetIds: List<Int>) { | ||
val sharedPreferences = requireContext().getSharedPreferences(Constants.WIDGETS_PREFS, Activity.MODE_PRIVATE) | ||
val editor = sharedPreferences.edit() | ||
val widgetIdsSet = widgetIds.map { it.toString() }.toSet() | ||
editor.putStringSet(Constants.APP_WIDGETS_ID, widgetIdsSet) | ||
editor.apply() | ||
} | ||
|
||
private fun loadWidgetIds(): List<Int> { | ||
val sharedPreferences = requireContext().getSharedPreferences(Constants.WIDGETS_PREFS, Activity.MODE_PRIVATE) | ||
val widgetIdsSet = sharedPreferences.getStringSet(Constants.APP_WIDGETS_ID, emptySet()) | ||
return widgetIdsSet?.map { it.toInt() } ?: emptyList() | ||
} | ||
|
||
override fun onDestroyView() { | ||
super.onDestroyView() | ||
_binding = null | ||
} | ||
} | ||
|
Oops, something went wrong.