Issue #5811: "Delete folder" menu in custom image selector

This commit is contained in:
why-lab 2024-10-16 21:46:59 +02:00
parent d0e64d7886
commit 1725304cce
8 changed files with 147 additions and 26 deletions

View file

@ -1,16 +1,12 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingElse" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="ConfusingElse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="reportWhenNoStatementFollow" value="true" /> <option name="reportWhenNoStatementFollow" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="ERROR" enabled_by_default="true" /> <inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExplicitThis" enabled="true" level="WEAK WARNING" enabled_by_default="true" /> <inspection_tool class="ExplicitThis" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_VARIABLES" value="true" /> <option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" /> <option name="REPORT_PARAMETERS" value="true" />
@ -25,13 +21,11 @@
<option name="ignoreInMatchingInstanceof" value="false" /> <option name="ignoreInMatchingInstanceof" value="false" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantFieldInitialization" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="RedundantFieldInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSerializable" value="false" /> <option name="ignoreSerializable" value="false" />
<option name="ignoreCloneable" value="false" /> <option name="ignoreCloneable" value="false" />
</inspection_tool> </inspection_tool>
<inspection_tool class="RedundantMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
@ -47,6 +41,5 @@
<inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
</profile> </profile>
</component> </component>

View file

@ -366,11 +366,11 @@ android {
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "17"
} }
buildToolsVersion buildToolsVersion buildToolsVersion buildToolsVersion

View file

@ -13,6 +13,11 @@ import android.view.Window
import android.widget.Button import android.widget.Button
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import android.view.Menu
import android.view.MenuItem
import android.widget.PopupMenu
import androidx.appcompat.app.AlertDialog
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -59,6 +64,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File import java.io.File
import java.lang.Integer.max import java.lang.Integer.max
import javax.inject.Inject import javax.inject.Inject
@ -147,6 +153,13 @@ class CustomSelectorActivity :
private var showPartialAccessIndicator by mutableStateOf(false) private var showPartialAccessIndicator by mutableStateOf(false)
/**
* Show delete button on folder
*/
private var showOverflowMenu = false
/** /**
* onCreate Activity, sets theme, initialises the view model, setup view. * onCreate Activity, sets theme, initialises the view model, setup view.
*/ */
@ -173,9 +186,9 @@ class CustomSelectorActivity :
} }
}, },
modifier = modifier =
Modifier Modifier
.padding(vertical = 8.dp, horizontal = 4.dp) .padding(vertical = 8.dp, horizontal = 4.dp)
.fillMaxWidth(), .fillMaxWidth(),
) )
} }
val view = binding.root val view = binding.root
@ -204,6 +217,7 @@ class CustomSelectorActivity :
} }
} }
override fun onRequestPermissionsResult( override fun onRequestPermissionsResult(
requestCode: Int, requestCode: Int,
permissions: Array<out String>, permissions: Array<out String>,
@ -414,7 +428,7 @@ class CustomSelectorActivity :
} }
/** /**
* Set up the toolbar, back listener, done listener. * Set up the toolbar, back listener, done listener, overflow menu listener.
*/ */
private fun setUpToolbar() { private fun setUpToolbar() {
val back: ImageButton = findViewById(R.id.back) val back: ImageButton = findViewById(R.id.back)
@ -423,16 +437,72 @@ class CustomSelectorActivity :
val limitError: ImageButton = findViewById(R.id.image_limit_error) val limitError: ImageButton = findViewById(R.id.image_limit_error)
limitError.visibility = View.INVISIBLE limitError.visibility = View.INVISIBLE
limitError.setOnClickListener { displayUploadLimitWarning() } limitError.setOnClickListener { displayUploadLimitWarning() }
val overflowMenu: ImageButton = findViewById(R.id.menu_overflow)
overflowMenu.visibility = if (showOverflowMenu) View.VISIBLE else View.INVISIBLE
// Set up popup menu when overflow menu is clicked
overflowMenu.setOnClickListener { showPopupMenu(overflowMenu) }
} }
private fun showPopupMenu(anchorView: View) {
val popupMenu = PopupMenu(this, anchorView)
popupMenu.menuInflater.inflate(R.menu.menu_custom_selector, popupMenu.menu)
// Handle menu item clicks
popupMenu.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.action_delete_folder -> {
deleteFolder() // Call the delete folder logic here
true
}
else -> false
}
}
popupMenu.show()
}
private fun deleteFolder() {
Timber.tag("FolderAction").d("Delete folder action triggered")
// Run on UI thread to ensure dialog shows correctly
runOnUiThread {
val builder = AlertDialog.Builder(this)
builder.setTitle("Delete Folder")
builder.setMessage("Are you sure you want to delete this folder?")
// Set the positive button to confirm deletion
builder.setPositiveButton("Delete") { dialog, _ ->
// Perform folder deletion here
Timber.tag("FolderAction").d("Folder deleted")
dialog.dismiss()
}
// Set the negative button to cancel
builder.setNegativeButton("Cancel") { dialog, _ ->
Timber.tag("FolderAction").d("Delete action cancelled")
dialog.dismiss() // Dismiss the dialog
}
// Show the AlertDialog
builder.create().show()
}
}
/** /**
* override on folder click, change the toolbar title on folder click. * override on folder click,
* change the toolbar title on folder click, make overflow menu visible
*/ */
override fun onFolderClick( override fun onFolderClick(
folderId: Long, folderId: Long,
folderName: String, folderName: String,
lastItemId: Long, lastItemId: Long
) { ) {
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.add(R.id.fragment_container, ImageFragment.newInstance(folderId, lastItemId)) .add(R.id.fragment_container, ImageFragment.newInstance(folderId, lastItemId))
@ -444,8 +514,14 @@ class CustomSelectorActivity :
bucketId = folderId bucketId = folderId
bucketName = folderName bucketName = folderName
isImageFragmentOpen = true isImageFragmentOpen = true
// Show the overflow menu only when a folder is clicked
showOverflowMenu = true
setUpToolbar()
} }
/** /**
* override Selected Images Change, update view model selected images and change UI. * override Selected Images Change, update view model selected images and change UI.
*/ */
@ -559,6 +635,10 @@ class CustomSelectorActivity :
isImageFragmentOpen = false isImageFragmentOpen = false
changeTitle(getString(R.string.custom_selector_title), 0) changeTitle(getString(R.string.custom_selector_title), 0)
} }
//hide overflow menu when not in folder
showOverflowMenu = false
setUpToolbar()
} }
/** /**
@ -616,9 +696,9 @@ fun partialStorageAccessIndicator(
OutlinedCard( OutlinedCard(
modifier = modifier, modifier = modifier,
colors = colors =
CardDefaults.cardColors( CardDefaults.cardColors(
containerColor = colorResource(R.color.primarySuperLightColor), containerColor = colorResource(R.color.primarySuperLightColor),
), ),
border = BorderStroke(0.5.dp, color = colorResource(R.color.primaryColor)), border = BorderStroke(0.5.dp, color = colorResource(R.color.primaryColor)),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
) { ) {
@ -631,9 +711,9 @@ fun partialStorageAccessIndicator(
onClick = onManage, onClick = onManage,
modifier = Modifier.align(Alignment.Bottom), modifier = Modifier.align(Alignment.Bottom),
colors = colors =
ButtonDefaults.buttonColors( ButtonDefaults.buttonColors(
containerColor = colorResource(R.color.primaryColor), containerColor = colorResource(R.color.primaryColor),
), ),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
) { ) {
Text( Text(
@ -655,9 +735,9 @@ fun partialStorageAccessIndicatorPreview() {
isVisible = true, isVisible = true,
onManage = {}, onManage = {},
modifier = modifier =
Modifier Modifier
.padding(vertical = 8.dp, horizontal = 4.dp) .padding(vertical = 8.dp, horizontal = 4.dp)
.fillMaxWidth(), .fillMaxWidth(),
) )
} }
} }

View file

@ -12,6 +12,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
@ -58,6 +59,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import timber.log.Timber;
public class SettingsFragment extends PreferenceFragmentCompat { public class SettingsFragment extends PreferenceFragmentCompat {
@ -80,6 +82,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
private ListPreference themeListPreference; private ListPreference themeListPreference;
private Preference descriptionLanguageListPreference; private Preference descriptionLanguageListPreference;
private Preference appUiLanguageListPreference; private Preference appUiLanguageListPreference;
private Preference showDeletionButtonPreference;
private String keyLanguageListPreference; private String keyLanguageListPreference;
private TextView recentLanguagesTextView; private TextView recentLanguagesTextView;
private View separator; private View separator;
@ -178,6 +181,18 @@ public class SettingsFragment extends PreferenceFragmentCompat {
} }
}); });
//
showDeletionButtonPreference = findPreference("displayDeletionButton");
if (showDeletionButtonPreference != null) {
showDeletionButtonPreference.setOnPreferenceChangeListener((preference, newValue) -> {
boolean isEnabled = (boolean) newValue;
// Save preference when user toggles the button
defaultKvStore.putBoolean("displayDeletionButton", isEnabled);
return true;
});
}
Preference betaTesterPreference = findPreference("becomeBetaTester"); Preference betaTesterPreference = findPreference("becomeBetaTester");
betaTesterPreference.setOnPreferenceClickListener(preference -> { betaTesterPreference.setOnPreferenceClickListener(preference -> {
Utils.handleWebUrl(getActivity(), Uri.parse(getResources().getString(R.string.beta_opt_in_link))); Utils.handleWebUrl(getActivity(), Uri.parse(getResources().getString(R.string.beta_opt_in_link)));

View file

@ -52,5 +52,20 @@
app:layout_constraintVertical_bias="1.0" app:layout_constraintVertical_bias="1.0"
app:srcCompat="@drawable/ic_error_red_24dp" /> app:srcCompat="@drawable/ic_error_red_24dp" />
<ImageButton
android:id="@+id/menu_overflow"
android:layout_width="48dp"
android:layout_height="0dp"
android:background="#00FFFFFF"
android:contentDescription="@string/menu_overflow_desc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
app:srcCompat="@drawable/ic_overflow" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</merge> </merge>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_delete_folder"
android:title="@string/delete_folder"
android:icon="@drawable/ic_delete_grey_700_24dp"
app:showAsAction="never" />
</menu>

View file

@ -117,6 +117,7 @@
<string name="categories_search_text_hint">Search categories</string> <string name="categories_search_text_hint">Search categories</string>
<string name="depicts_search_text_hint">Search for items that your media depicts (mountain, Taj Mahal, etc.)</string> <string name="depicts_search_text_hint">Search for items that your media depicts (mountain, Taj Mahal, etc.)</string>
<string name="menu_save_categories">Save</string> <string name="menu_save_categories">Save</string>
<string name="menu_overflow_desc">Overflow menu</string>
<string name="refresh_button">Refresh</string> <string name="refresh_button">Refresh</string>
<string name="display_list_button">List</string> <string name="display_list_button">List</string>
<string name="contributions_subtitle_zero">(No uploads yet)</string> <string name="contributions_subtitle_zero">(No uploads yet)</string>
@ -831,4 +832,5 @@ Upload your first media by tapping on the add button.</string>
<string name="pending">Pending</string> <string name="pending">Pending</string>
<string name="failed">Failed</string> <string name="failed">Failed</string>
<string name="could_not_load_place_data">Could not load place data</string> <string name="could_not_load_place_data">Could not load place data</string>
<string name="delete_folder">Delete Folder</string>
</resources> </resources>

View file

@ -51,6 +51,13 @@
android:summary="@string/display_campaigns_explanation" android:summary="@string/display_campaigns_explanation"
android:title="@string/display_campaigns" /> android:title="@string/display_campaigns" />
<SwitchPreference
android:defaultValue="false"
android:key="displayDeletionButton"
app:singleLineTitle="false"
android:summary="Enable the &quot;Delete folder&quot; button in the custom picker"
android:title="Show Deletion Button" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory