Feat: Basic starter to add Widget support.
This commit is contained in:
13
.vscode/easycode.ignore
vendored
Normal file
13
.vscode/easycode.ignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
node_modules/
|
||||
dist/
|
||||
vendor/
|
||||
cache/
|
||||
.*/
|
||||
*.min.*
|
||||
*.test.*
|
||||
*.spec.*
|
||||
*.bundle.*
|
||||
*.bundle-min.*
|
||||
*.*.js
|
||||
*.*.ts
|
||||
*.log
|
||||
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
AsterLauncher Copyright (C) 2024 neophtex
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
@@ -51,6 +51,7 @@ android {
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
applicationVariants.all {
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
|
||||
<uses-permission android:name="android.permission.BIND_APPWIDGET"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
|
||||
@@ -4,6 +4,9 @@ object Constants {
|
||||
const val PACKAGE_NAME = "app.easy.launcher"
|
||||
const val PACKAGE_NAME_DEBUG = "$PACKAGE_NAME.debug"
|
||||
|
||||
const val WIDGETS_PREFS = "EasyLauncherWidgets.pref"
|
||||
const val APP_WIDGETS_ID = "APP_WIDGETS_ID"
|
||||
|
||||
const val PREFS_FILENAME = "EasyLauncher.pref"
|
||||
const val FIRST_LAUNCH = "FIRST_LAUNCH"
|
||||
const val SHOW_DATE = "SHOW_DATE"
|
||||
@@ -39,8 +42,6 @@ object Constants {
|
||||
const val HOME_TIME_ALIGNMENT = "HOME_TIME_ALIGNMENT"
|
||||
const val HOME_APP_ALIGNMENT = "HOME_APP_ALIGNMENT"
|
||||
const val HOME_DAILY_WORD_ALIGNMENT = "HOME_DAILY_WORD_ALIGNMENT"
|
||||
const val HOME_DRAW_ALIGNMENT = "HOME_DRAW_ALIGNMENT"
|
||||
const val HOME_BATTERY_ALIGNMENT = "HOME_BATTERY_ALIGNMENT"
|
||||
|
||||
const val NOTIFICATION_SERVICE = "statusbar"
|
||||
const val NOTIFICATION_MANAGER = "android.app.StatusBarManager"
|
||||
@@ -61,5 +62,6 @@ object Constants {
|
||||
const val URL_GOOGLE_PLAY_STORE = "https://play.google.com/store/search?c=apps&q"
|
||||
const val APP_GOOGLE_PLAY_STORE = "market://search?c=apps&q"
|
||||
|
||||
const val APP_WIDGET_HOST_ID = 1024
|
||||
const val REQUEST_CODE_ENABLE_ADMIN = 123
|
||||
}
|
||||
@@ -101,11 +101,11 @@ class AppHelper @Inject constructor() {
|
||||
fun dayNightMod(context: Context, view: View) {
|
||||
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
Configuration.UI_MODE_NIGHT_YES -> {
|
||||
view.setBackgroundColor(context.resources.getColor(R.color.blackTrans50, context.theme))
|
||||
view.setBackgroundColor(context.resources.getColor(R.color.blackTrans25, context.theme))
|
||||
}
|
||||
|
||||
Configuration.UI_MODE_NIGHT_NO -> {
|
||||
view.setBackgroundColor(context.resources.getColor(R.color.whiteTrans50, context.theme))
|
||||
view.setBackgroundColor(context.resources.getColor(R.color.whiteTrans25, context.theme))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun observeUI() {
|
||||
binding.pager.currentItem = 0
|
||||
binding.pager.currentItem = 1
|
||||
preferenceViewModel.setShowStatusBar(preferenceHelper.showStatusBar)
|
||||
preferenceViewModel.showStatusBarLiveData.observe(this) {
|
||||
if (it) appHelper.showStatusBar(this.window)
|
||||
|
||||
@@ -40,7 +40,7 @@ class DrawFragment : Fragment(), OnItemClickedListener.OnAppsClickedListener,
|
||||
OnItemClickedListener.OnAppLongClickedListener,
|
||||
OnItemClickedListener.BottomSheetDismissListener,
|
||||
OnItemClickedListener.OnAppStateClickListener,
|
||||
FingerprintHelper.Callback{
|
||||
FingerprintHelper.Callback {
|
||||
private var _binding: FragmentDrawBinding? = null
|
||||
|
||||
private val binding get() = _binding!!
|
||||
@@ -181,14 +181,12 @@ class DrawFragment : Fragment(), OnItemClickedListener.OnAppsClickedListener,
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
observeDrawerApps()
|
||||
binding.drawAdapter.scrollToPosition(0)
|
||||
if (preferenceHelper.automaticKeyboard) binding.searchViewText.showKeyboard()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
observeDrawerApps()
|
||||
binding.drawAdapter.scrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
||||
@@ -6,12 +6,14 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.github.droidworksstudio.launcher.ui.drawer.DrawFragment
|
||||
import com.github.droidworksstudio.launcher.ui.home.HomeFragment
|
||||
import com.github.droidworksstudio.launcher.ui.widgetmanager.WidgetManagerFragment
|
||||
|
||||
|
||||
class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
|
||||
FragmentStateAdapter(fragmentManager, lifecycle) {
|
||||
|
||||
private val fragments: ArrayList<Fragment> = arrayListOf(
|
||||
WidgetManagerFragment(),
|
||||
HomeFragment(),
|
||||
DrawFragment(),
|
||||
)
|
||||
@@ -23,5 +25,4 @@ class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return fragments[position]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.github.droidworksstudio.launcher.ui.widgetmanager
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
|
||||
data class WidgetItem(val appWidgetId: Int, val appWidgetInfo: AppWidgetProviderInfo?)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.github.droidworksstudio.launcher.ui.widgetmanager
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Button
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.github.droidworksstudio.launcher.Constants
|
||||
import com.github.droidworksstudio.launcher.R
|
||||
|
||||
class WidgetOptionsDialogFragment : DialogFragment() {
|
||||
|
||||
interface WidgetOptionsListener {
|
||||
fun onRemoveWidget(appWidgetId: Int)
|
||||
fun onOpenAppWidget(appWidgetId: Int)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val appWidgetId = arguments?.getInt(ARG_APP_WIDGET_ID) ?: return super.onCreateDialog(savedInstanceState)
|
||||
|
||||
val builder = AlertDialog.Builder(requireContext())
|
||||
builder.setTitle("Widget Options")
|
||||
.setItems(arrayOf("Remove Widget", "Open Widget")) { _, which ->
|
||||
val listener = parentFragment as? WidgetOptionsListener
|
||||
when (which) {
|
||||
0 -> listener?.onRemoveWidget(appWidgetId)
|
||||
1 -> listener?.onOpenAppWidget(appWidgetId)
|
||||
}
|
||||
}
|
||||
return builder.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_APP_WIDGET_ID = Constants.APP_WIDGETS_ID
|
||||
|
||||
fun newInstance(appWidgetId: Int): WidgetOptionsDialogFragment {
|
||||
val fragment = WidgetOptionsDialogFragment()
|
||||
val args = Bundle().apply {
|
||||
putInt(ARG_APP_WIDGET_ID, appWidgetId)
|
||||
}
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
26
app/src/main/res/layout/fragment_widget_manager.xml
Normal file
26
app/src/main/res/layout/fragment_widget_manager.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/draw_background"
|
||||
tools:context=".ui.widgetmanager.WidgetManagerFragment">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/widget_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:longClickable="true"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/widget_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"/>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
6
app/src/main/res/layout/item_widget.xml
Normal file
6
app/src/main/res/layout/item_widget.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/widget_frame"
|
||||
android:padding="8dp"/>
|
||||
29
app/src/main/res/layout/widget_options_dialog.xml
Normal file
29
app/src/main/res/layout/widget_options_dialog.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_open_app"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Open App"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:backgroundTint="?attr/colorAccent"
|
||||
app:backgroundTint="?attr/colorAccent"
|
||||
app:rippleColor="?attr/colorAccent" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_remove_widget"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Remove Widget"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:backgroundTint="?attr/colorAccent"
|
||||
app:backgroundTint="?attr/colorAccent"
|
||||
app:rippleColor="?attr/colorAccent" />
|
||||
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user