Skip to content

Commit 776b217

Browse files
committed
refactor: improve accessibility in various places
- Running apps page header and selection mode - Main page selection mode - Log viewer selection mode - Action content descriptions - Floating action buttons selectable before the list - Path navigation in file manager - File manager selection mode - Debloater selection mode - Clicking "View logs" button supporting automatic navigation to the logs Signed-off-by: Muntashir Al-Islam <muntashirakon@riseup.net>
1 parent 06d3157 commit 776b217

20 files changed

Lines changed: 218 additions & 71 deletions

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@
109109
<uses-permission
110110
android:name="android.permission.MANAGE_SENSORS"
111111
tools:ignore="ProtectedPermissions" />
112+
<uses-permission
113+
android:name="android.permission.NETWORK_SETTINGS"
114+
tools:ignore="ProtectedPermissions" />
112115
<uses-permission
113116
android:name="android.permission.PACKAGE_USAGE_STATS"
114117
tools:ignore="ProtectedPermissions" />

app/src/main/java/io/github/muntashirakon/AppManager/batchops/BatchOpsResultsActivity.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.github.muntashirakon.AppManager.utils.PackageUtils;
3434
import io.github.muntashirakon.AppManager.utils.RestartUtils;
3535
import io.github.muntashirakon.AppManager.utils.UIUtils;
36+
import io.github.muntashirakon.util.AccessibilityUtils;
3637

3738
public class BatchOpsResultsActivity extends BaseActivity {
3839
private RecyclerView mRecyclerView;
@@ -58,7 +59,10 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) {
5859
MaterialButton logToggler = findViewById(R.id.action_view_logs);
5960
mLogViewer = findViewById(R.id.text);
6061
mLogViewer.setKeyListener(null);
61-
logToggler.setOnClickListener(v -> mLogViewer.setVisibility(View.VISIBLE));
62+
logToggler.setOnClickListener(v -> {
63+
mLogViewer.setVisibility(View.VISIBLE);
64+
AccessibilityUtils.requestAccessibilityFocus(mLogViewer);
65+
});
6266
handleIntent(getIntent());
6367
}
6468

app/src/main/java/io/github/muntashirakon/AppManager/debloat/DebloaterRecyclerViewAdapter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import io.github.muntashirakon.AppManager.R;
2929
import io.github.muntashirakon.AppManager.utils.appearance.ColorCodes;
30+
import io.github.muntashirakon.util.AccessibilityUtils;
3031
import io.github.muntashirakon.util.AdapterUtils;
3132
import io.github.muntashirakon.widget.MultiSelectionView;
3233

@@ -120,12 +121,17 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
120121
holder.labelView.setText(label);
121122
holder.itemView.setOnLongClickListener(v -> {
122123
toggleSelection(position);
124+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
123125
return true;
124126
});
125-
holder.iconView.setOnClickListener(v -> toggleSelection(position));
127+
holder.iconView.setOnClickListener(v -> {
128+
toggleSelection(position);
129+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
130+
});
126131
holder.itemView.setOnClickListener(v -> {
127132
if (isInSelectionMode()) {
128133
toggleSelection(position);
134+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
129135
} else {
130136
BloatwareDetailsDialog dialog = BloatwareDetailsDialog.getInstance(debloatObject.packageName);
131137
dialog.show(mActivity.getSupportFragmentManager(), BloatwareDetailsDialog.TAG);

app/src/main/java/io/github/muntashirakon/AppManager/fm/FmAdapter.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import io.github.muntashirakon.AppManager.utils.UIUtils;
4141
import io.github.muntashirakon.AppManager.utils.Utils;
4242
import io.github.muntashirakon.io.Path;
43+
import io.github.muntashirakon.util.AccessibilityUtils;
4344
import io.github.muntashirakon.util.AdapterUtils;
4445
import io.github.muntashirakon.widget.MultiSelectionView;
4546

@@ -81,6 +82,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
8182
holder.itemView.setOnClickListener(v -> {
8283
if (isInSelectionMode()) {
8384
toggleSelection(position);
85+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
8486
return;
8587
}
8688
mViewModel.loadFiles(item.path.getUri());
@@ -89,6 +91,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
8991
holder.itemView.setOnClickListener(v -> {
9092
if (isInSelectionMode()) {
9193
toggleSelection(position);
94+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
9295
return;
9396
}
9497
// TODO: 16/11/22 Retrieve default open with from DB and open the file with it
@@ -101,9 +104,13 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
101104
// Set background colors
102105
holder.itemView.setCardBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), android.R.color.transparent));
103106
// Set selections
104-
holder.icon.setOnClickListener(v -> toggleSelection(position));
107+
holder.icon.setOnClickListener(v -> {
108+
toggleSelection(position);
109+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
110+
});
105111
// Set actions
106-
holder.action.setOnClickListener(v -> displayActions(holder.action, item));
112+
PopupMenu popupMenu = getPopupMenu(holder.action, item, position);
113+
holder.action.setOnClickListener(v -> popupMenu.show());
107114
holder.itemView.setOnLongClickListener(v -> {
108115
// Long click listener: Select/deselect an app.
109116
// 1) Turn selection mode on if this is the first item in the selection list
@@ -123,7 +130,10 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
123130
if (lastSelectedItemPosition >= 0) {
124131
// Select from last selection to this selection
125132
selectRange(lastSelectedItemPosition, position);
126-
} else toggleSelection(position);
133+
} else {
134+
toggleSelection(position);
135+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
136+
}
127137
return true;
128138
});
129139
super.onBindViewHolder(holder, position);
@@ -211,7 +221,7 @@ protected int getTotalItemCount() {
211221
return mAdapterList.size();
212222
}
213223

214-
private void displayActions(View anchor, FmItem item) {
224+
private PopupMenu getPopupMenu(@NonNull View anchor, @NonNull FmItem item, int position) {
215225
PopupMenu popupMenu = new PopupMenu(anchor.getContext(), anchor);
216226
popupMenu.setForceShowIcon(true);
217227
popupMenu.inflate(R.menu.fragment_fm_item_actions);
@@ -222,6 +232,7 @@ private void displayActions(View anchor, FmItem item) {
222232
MenuItem renameAction = menu.findItem(R.id.action_rename);
223233
MenuItem deleteAction = menu.findItem(R.id.action_delete);
224234
MenuItem shareAction = menu.findItem(R.id.action_share);
235+
MenuItem selectAction = menu.findItem(R.id.action_select);
225236
// Disable actions based on criteria
226237
boolean canRead = item.path.canRead();
227238
boolean canWrite = item.path.canWrite();
@@ -237,19 +248,19 @@ private void displayActions(View anchor, FmItem item) {
237248
fragment.show(mFmActivity.getSupportFragmentManager(), OpenWithDialogFragment.TAG);
238249
return true;
239250
});
240-
menu.findItem(R.id.action_cut).setOnMenuItemClickListener(menuItem -> {
251+
cutAction.setOnMenuItemClickListener(menuItem -> {
241252
FmTasks.FmTask fmTask = new FmTasks.FmTask(FmTasks.FmTask.TYPE_CUT, Collections.singletonList(item.path));
242253
FmTasks.getInstance().enqueue(fmTask);
243254
UIUtils.displayShortToast(R.string.copied_to_clipboard);
244255
return false;
245256
});
246-
menu.findItem(R.id.action_copy).setOnMenuItemClickListener(menuItem -> {
257+
copyAction.setOnMenuItemClickListener(menuItem -> {
247258
FmTasks.FmTask fmTask = new FmTasks.FmTask(FmTasks.FmTask.TYPE_COPY, Collections.singletonList(item.path));
248259
FmTasks.getInstance().enqueue(fmTask);
249260
UIUtils.displayShortToast(R.string.copied_to_clipboard);
250261
return false;
251262
});
252-
menu.findItem(R.id.action_rename).setOnMenuItemClickListener(menuItem -> {
263+
renameAction.setOnMenuItemClickListener(menuItem -> {
253264
RenameDialogFragment dialog = RenameDialogFragment.getInstance(item.path.getName(), (prefix, extension) -> {
254265
String displayName;
255266
if (!TextUtils.isEmpty(extension)) {
@@ -267,7 +278,7 @@ private void displayActions(View anchor, FmItem item) {
267278
dialog.show(mFmActivity.getSupportFragmentManager(), RenameDialogFragment.TAG);
268279
return false;
269280
});
270-
menu.findItem(R.id.action_delete).setOnMenuItemClickListener(menuItem -> {
281+
deleteAction.setOnMenuItemClickListener(menuItem -> {
271282
new MaterialAlertDialogBuilder(mFmActivity)
272283
.setTitle(mFmActivity.getString(R.string.delete_filename, item.path.getName()))
273284
.setMessage(R.string.are_you_sure)
@@ -283,10 +294,16 @@ private void displayActions(View anchor, FmItem item) {
283294
.show();
284295
return true;
285296
});
286-
menu.findItem(R.id.action_share).setOnMenuItemClickListener(menuItem -> {
297+
shareAction.setOnMenuItemClickListener(menuItem -> {
287298
mViewModel.shareFiles(Collections.singletonList(item.path));
288299
return true;
289300
});
301+
selectAction.setOnMenuItemClickListener(menuItem -> {
302+
select(position);
303+
notifySelectionChange();
304+
notifyItemChanged(position, AdapterUtils.STUB);
305+
return true;
306+
});
290307
boolean isVfs = mViewModel.getOptions().isVfs();
291308
menu.findItem(R.id.action_shortcut)
292309
// TODO: 31/5/23 Enable creating shortcuts for VFS
@@ -312,7 +329,7 @@ private void displayActions(View anchor, FmItem item) {
312329
mViewModel.getDisplayPropertiesLiveData().setValue(item.path.getUri());
313330
return true;
314331
});
315-
popupMenu.show();
332+
return popupMenu;
316333
}
317334

318335
protected static class ViewHolder extends MultiSelectionView.ViewHolder {

app/src/main/java/io/github/muntashirakon/AppManager/fm/FmPathListAdapter.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,15 @@ public PathHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
100100

101101
@Override
102102
public void onBindViewHolder(@NonNull PathHolder holder, int position) {
103+
String actualPathPart = mPathParts.get(position);
103104
String pathPart;
104105
if (position == 0) {
105-
pathPart = mAlternativeRootName != null ? mAlternativeRootName : mPathParts.get(position);
106-
} else pathPart = "» " + mPathParts.get(position);
106+
pathPart = mAlternativeRootName != null ? mAlternativeRootName : actualPathPart;
107+
} else pathPart = "» " + actualPathPart;
107108
holder.textView.setText(pathPart);
109+
if (position == 0 && pathPart.equals("/")) {
110+
holder.itemView.setContentDescription(holder.itemView.getContext().getString(R.string.root));
111+
} else holder.itemView.setContentDescription(actualPathPart);
108112
holder.itemView.setOnClickListener(v -> {
109113
if (mCurrentPosition != position) {
110114
mViewModel.loadFiles(calculateUri(position));

app/src/main/java/io/github/muntashirakon/AppManager/logcat/LogViewerRecyclerAdapter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import io.github.muntashirakon.AppManager.logs.Log;
3535
import io.github.muntashirakon.AppManager.settings.Prefs;
3636
import io.github.muntashirakon.AppManager.utils.Utils;
37+
import io.github.muntashirakon.util.AccessibilityUtils;
3738
import io.github.muntashirakon.util.AdapterUtils;
3839
import io.github.muntashirakon.widget.MultiSelectionView;
3940

@@ -409,6 +410,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
409410
holder.itemView.setOnClickListener(v -> {
410411
if (isInSelectionMode()) {
411412
toggleSelection(position);
413+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
412414
} else {
413415
LogLine line = holder.logLine;
414416
line.setExpanded(!line.isExpanded());
@@ -424,7 +426,10 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
424426
if (lastSelectedItemPosition >= 0) {
425427
// Select from last selection to this selection
426428
selectRange(lastSelectedItemPosition, position);
427-
} else toggleSelection(position);
429+
} else {
430+
toggleSelection(position);
431+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
432+
}
428433
return true;
429434
}
430435
PopupMenu popupMenu = new PopupMenu(v.getContext(), v);
@@ -448,6 +453,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
448453
.setIcon(R.drawable.ic_check_circle)
449454
.setOnMenuItemClickListener(menuItem -> {
450455
toggleSelection(position);
456+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
451457
return true;
452458
});
453459
popupMenu.show();

app/src/main/java/io/github/muntashirakon/AppManager/main/MainRecyclerAdapter.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import io.github.muntashirakon.AppManager.utils.appearance.ColorCodes;
6161
import io.github.muntashirakon.dialog.SearchableItemsDialogBuilder;
6262
import io.github.muntashirakon.io.Paths;
63+
import io.github.muntashirakon.util.AccessibilityUtils;
6364
import io.github.muntashirakon.util.AdapterUtils;
6465
import io.github.muntashirakon.widget.MultiSelectionView;
6566

@@ -189,6 +190,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
189190
// If selection mode is on, select/deselect the current item instead of the default behaviour
190191
if (isInSelectionMode()) {
191192
toggleSelection(position);
193+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
192194
return;
193195
}
194196
handleClick(item);
@@ -203,11 +205,17 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
203205
if (lastSelectedItemPosition >= 0) {
204206
// Select from last selection to this selection
205207
selectRange(lastSelectedItemPosition, position);
206-
} else toggleSelection(position);
208+
} else {
209+
toggleSelection(position);
210+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
211+
}
207212
}
208213
return true;
209214
});
210-
holder.icon.setOnClickListener(v -> toggleSelection(position));
215+
holder.icon.setOnClickListener(v -> {
216+
toggleSelection(position);
217+
AccessibilityUtils.requestAccessibilityFocus(holder.itemView);
218+
});
211219
// Box-stroke colors: uninstalled > disabled > force-stopped > regular
212220
if (!item.isInstalled) {
213221
cardView.setStrokeColor(ColorCodes.getAppUninstalledIndicatorColor(context));

0 commit comments

Comments
 (0)