Delete multiple app in the same time
Show application names in main view
This commit is contained in:
parent
ac30bf8aa8
commit
504b58ecec
|
@ -13,3 +13,4 @@
|
|||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
.idea
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package org.unifiedpush.distributor.nextpush.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.util.SparseBooleanArray
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isGone
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.unifiedpush.distributor.nextpush.R
|
||||
import org.unifiedpush.distributor.nextpush.utils.TAG
|
||||
|
||||
data class App(
|
||||
val token: String,
|
||||
val packageId: String
|
||||
)
|
||||
|
||||
class AppListAdapter(context: Context, private val resource: Int, apps: List<App>) : ArrayAdapter<App>(context, resource, apps) {
|
||||
private var selectedItemsIds = SparseBooleanArray()
|
||||
private val inflater = LayoutInflater.from(context)
|
||||
|
||||
private class ViewHolder {
|
||||
var name: TextView? = null
|
||||
var packageId: TextView? = null
|
||||
}
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
var viewHolder: ViewHolder? = null
|
||||
val convertView = convertView?.apply {
|
||||
viewHolder = tag as ViewHolder
|
||||
} ?: run {
|
||||
val rConvertView = inflater.inflate(resource, parent, false)
|
||||
viewHolder = ViewHolder().apply {
|
||||
this.name = rConvertView.findViewById(R.id.item_app_name) as TextView
|
||||
this.packageId = rConvertView.findViewById(R.id.item_app_id) as TextView
|
||||
}
|
||||
rConvertView.apply {
|
||||
tag = viewHolder
|
||||
}
|
||||
}
|
||||
getItem(position)?.let {
|
||||
try {
|
||||
val ai = if (Build.VERSION.SDK_INT >= 33) {
|
||||
context.packageManager.getApplicationInfo(
|
||||
it.packageId,
|
||||
PackageManager.ApplicationInfoFlags.of(
|
||||
PackageManager.GET_META_DATA.toLong()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
context.packageManager.getApplicationInfo(it.packageId, 0)
|
||||
}
|
||||
viewHolder?.name?.text = context.packageManager.getApplicationLabel(ai)
|
||||
viewHolder?.packageId?.text = it.packageId
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.e(TAG, "Could not resolve app name", e)
|
||||
viewHolder?.name?.text = it.packageId
|
||||
viewHolder?.packageId?.isGone = true
|
||||
}
|
||||
}
|
||||
if (selectedItemsIds.get(position)) {
|
||||
convertView?.setBackgroundColor(
|
||||
MaterialColors.getColor(convertView, R.attr.colorOnTertiary)
|
||||
)
|
||||
} else {
|
||||
convertView?.setBackgroundResource(0)
|
||||
}
|
||||
return convertView
|
||||
}
|
||||
|
||||
fun toggleSelection(position: Int) {
|
||||
selectView(position, !selectedItemsIds.get(position))
|
||||
}
|
||||
|
||||
fun removeSelection() {
|
||||
selectedItemsIds = SparseBooleanArray()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun selectView(position: Int, value: Boolean) {
|
||||
selectedItemsIds.put(position, value)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun getSelectedIds(): SparseBooleanArray {
|
||||
return selectedItemsIds
|
||||
}
|
||||
}
|
|
@ -6,15 +6,17 @@ import android.os.Bundle
|
|||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.ActionMode
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.* // ktlint-disable no-wildcard-imports
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import android.widget.AbsListView.MultiChoiceModeListener
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.util.size
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.setPadding
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.unifiedpush.distributor.nextpush.R
|
||||
import org.unifiedpush.distributor.nextpush.account.Account
|
||||
import org.unifiedpush.distributor.nextpush.account.Account.getAccount
|
||||
|
@ -34,6 +36,9 @@ import java.lang.String.format
|
|||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var listView: ListView
|
||||
|
||||
// if the unregister dialog is shown, we prevent the list to be reset
|
||||
private var preventListReset = false
|
||||
private var lastClickTime = 0L
|
||||
private var clickCount = 0
|
||||
|
||||
|
@ -62,9 +67,13 @@ class MainActivity : AppCompatActivity() {
|
|||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
if (preventListReset) {
|
||||
preventListReset = false
|
||||
} else {
|
||||
setListView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
|
@ -128,44 +137,91 @@ class MainActivity : AppCompatActivity() {
|
|||
private fun setListView() {
|
||||
listView = findViewById(R.id.applications_list)
|
||||
|
||||
val tokenList = emptyList<String>().toMutableList()
|
||||
val appList = emptyList<String>().toMutableList()
|
||||
val appList = emptyList<App>().toMutableList()
|
||||
|
||||
getDb(this).let { db ->
|
||||
db.listTokens().forEach {
|
||||
tokenList.add(it)
|
||||
appList.add(db.getPackageName(it) ?: it)
|
||||
appList.add(
|
||||
App(token = it, packageId = db.getPackageName(it) ?: it)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
listView.adapter = ArrayAdapter(
|
||||
val editListAdapter = AppListAdapter(
|
||||
this,
|
||||
android.R.layout.simple_list_item_1,
|
||||
R.layout.item_app,
|
||||
appList
|
||||
)
|
||||
|
||||
listView.setOnItemLongClickListener(
|
||||
fun(_: AdapterView<*>, _: View, position: Int, _: Long): Boolean {
|
||||
val alert: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(
|
||||
this
|
||||
)
|
||||
alert.setTitle("Unregistering")
|
||||
alert.setMessage("Are you sure to unregister ${appList[position]} ?")
|
||||
alert.setPositiveButton("YES") { dialog, _ ->
|
||||
val connectorToken = tokenList[position]
|
||||
deleteApp(this, connectorToken) {
|
||||
Log.d(TAG, "Unregistration is finished")
|
||||
listView.adapter = editListAdapter
|
||||
listView.choiceMode = ListView.CHOICE_MODE_MULTIPLE_MODAL
|
||||
|
||||
listView.setMultiChoiceModeListener(object : MultiChoiceModeListener {
|
||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||
editListAdapter.removeSelection()
|
||||
}
|
||||
|
||||
override fun onItemCheckedStateChanged(
|
||||
mode: ActionMode,
|
||||
position: Int,
|
||||
id: Long,
|
||||
checked: Boolean
|
||||
) {
|
||||
val checkedCount = listView.checkedItemCount
|
||||
mode.title = "$checkedCount selected"
|
||||
editListAdapter.toggleSelection(position)
|
||||
}
|
||||
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
|
||||
mode.menuInflater.inflate(R.menu.menu_delete, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
Log.d(TAG, "Action clicked")
|
||||
return when (item.itemId) {
|
||||
R.id.action_delete -> {
|
||||
Log.d(TAG, "deleting")
|
||||
val selected = editListAdapter.getSelectedIds()
|
||||
val alert = MaterialAlertDialogBuilder(this@MainActivity)
|
||||
alert.setTitle(getString(R.string.dialog_unregistering_title))
|
||||
alert.setMessage(getString(R.string.dialog_unregistering_content).format(selected.size))
|
||||
alert.setPositiveButton(getString(R.string.dialog_yes)) { dialog, _ ->
|
||||
var i = selected.size - 1
|
||||
while (i >= 0) {
|
||||
if (selected.valueAt(i)) {
|
||||
editListAdapter.getItem(selected.keyAt(i))?.let {
|
||||
deleteApp(this@MainActivity, it.token) {
|
||||
Log.d(TAG, "${it.packageId} unregistered")
|
||||
editListAdapter.remove(it)
|
||||
this@MainActivity.runOnUiThread {
|
||||
setListView()
|
||||
}
|
||||
}
|
||||
}
|
||||
i--
|
||||
}
|
||||
}
|
||||
preventListReset = false
|
||||
dialog.dismiss()
|
||||
mode.finish()
|
||||
}
|
||||
alert.setNegativeButton("NO") { dialog, _ -> dialog.dismiss() }
|
||||
alert.setNegativeButton(getString(R.string.dialog_no)) { dialog, _ -> dialog.dismiss() }
|
||||
alert.setOnCancelListener {
|
||||
Log.d(TAG, "Cancelled")
|
||||
}
|
||||
preventListReset = true
|
||||
alert.show()
|
||||
return true
|
||||
true
|
||||
}
|
||||
)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setDebugInformationListener() {
|
||||
|
@ -182,7 +238,7 @@ class MainActivity : AppCompatActivity() {
|
|||
text = getDebugInfo()
|
||||
setPadding(dpAsPixels.toInt())
|
||||
}
|
||||
AlertDialog.Builder(this)
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Debug information")
|
||||
.setView(showText)
|
||||
.setCancelable(false)
|
||||
|
@ -191,7 +247,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
.show()
|
||||
|
||||
clickCount = 0 // Réinitialisez le compteur après l'affichage de la popup
|
||||
clickCount = 0 // Reset count after showing the dialog
|
||||
}
|
||||
} else {
|
||||
clickCount = 1
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall" >
|
||||
<TextView
|
||||
android:id="@+id/item_app_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
android:gravity="center_vertical"/>
|
||||
<TextView
|
||||
android:id="@+id/item_app_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:gravity="center_vertical" />
|
||||
</LinearLayout>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:icon="@drawable/ic_delete_24"
|
||||
android:title="@string/action_delete"
|
||||
android:iconTint="@color/white" />
|
||||
</menu>
|
|
@ -2,6 +2,13 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="org.unifiedpush.distributor.nextpush.activities.MainActivity">
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:icon="@drawable/ic_delete_24"
|
||||
android:title="@string/action_delete"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_restart"
|
||||
android:orderInCategory="100"
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="colorOnTertiary">@color/dark_gray</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
|
@ -7,5 +7,7 @@
|
|||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="light_gray">#FFEFEFEF</color>
|
||||
<color name="dark_gray">#FF4F4F4F</color>
|
||||
<color name="nextcloud">#0F9AE6</color>
|
||||
</resources>
|
|
@ -39,4 +39,9 @@
|
|||
<string name="login_show_password_img_description">Show password</string>
|
||||
<string name="button_disable_optimisation">Disable optimisation</string>
|
||||
<string name="card_disable_optimisation_description">To ensure the app functions properly, it is important to disable battery optimization. This will prevent the app from being put to sleep and causing delays in notifications.</string>
|
||||
<string name="action_delete">Delete</string>
|
||||
<string name="dialog_yes">YES</string>
|
||||
<string name="dialog_no">NO</string>
|
||||
<string name="dialog_unregistering_title">Unregistering</string>
|
||||
<string name="dialog_unregistering_content">Are you sure to unregister %d app(s)?</string>
|
||||
</resources>
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="colorOnTertiary">@color/light_gray</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
|
|
Loading…
Reference in New Issue