Skip to content

Commit a822e2d

Browse files
Updated library to v10.0.0 with full support for Android 11-14 storage changes
1 parent fadbd34 commit a822e2d

23 files changed

Lines changed: 2068 additions & 1171 deletions

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ hs_err_pid*
8282
/.idea/libraries
8383
.DS_Store
8484
/captures
85-
**/*.iml
85+
**/*.iml
86+
library/build
87+
sample/build

Readme.md

Lines changed: 1091 additions & 294 deletions
Large diffs are not rendered by default.

build.gradle

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ buildscript {
88
}
99
}
1010
dependencies {
11-
classpath 'com.android.tools.build:gradle:8.9.1'
12-
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
13-
classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
14-
15-
// NOTE: Do not place your application dependencies here; they belong
16-
// in the individual module build.gradle files
11+
classpath 'com.android.tools.build:gradle:9.2.1'
12+
classpath 'io.github.gradle-nexus:publish-plugin:2.0.0'
1713
}
1814
}
1915
apply plugin: 'io.github.gradle-nexus.publish-plugin'

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#Wed Mar 26 19:42:46 IST 2025
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
4-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
4+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
55
networkTimeout=10000
66
validateDistributionUrl=true
77
zipStoreBase=GRADLE_USER_HOME

library/build.gradle

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ group='com.github.TutorialsAndroid'
44

55
ext {
66
PUBLISH_GROUP_ID = 'io.github.tutorialsandroid'
7-
PUBLISH_VERSION = '9.2.6'
7+
PUBLISH_VERSION = '10.0.0'
88
PUBLISH_ARTIFACT_ID = 'filepicker'
99
PUBLISH_DESCRIPTION = 'Android Library to select files/directories from Device Storage'
1010
PUBLISH_URL = 'https://github.com/TutorialsAndroid/FilePicker'
@@ -21,22 +21,29 @@ ext {
2121
PUBLISH_SCM_URL =
2222
'https://github.com/tutorialsandroid/filepicker/tree/master'
2323
}
24-
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
2524

2625
android {
27-
compileSdk 35
26+
namespace 'com.developer.filepicker'
27+
28+
compileSdk 37
2829

2930
defaultConfig {
30-
minSdkVersion 21
31-
targetSdkVersion 35
31+
minSdk 23
32+
targetSdk 37
3233
}
3334
buildTypes {
3435
release {
3536
minifyEnabled false
36-
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
37+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
38+
}
39+
}
40+
41+
publishing {
42+
singleVariant("release") {
43+
withSourcesJar()
44+
withJavadocJar()
3745
}
3846
}
39-
namespace 'com.developer.filepicker'
4047

4148
compileOptions {
4249
sourceCompatibility JavaVersion.VERSION_11
@@ -46,3 +53,5 @@ android {
4653
abortOnError false
4754
}
4855
}
56+
57+
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.developer.filepicker.controller;
22

33
/**
4-
* @author akshay sunil masram
4+
* Callback invoked when the user confirms selected file/folder paths.
55
*/
66
public interface DialogSelectionListener {
77
void onSelectedFilePaths(String[] files);
8-
}
8+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.developer.filepicker.controller;
22

33
/**
4-
* @author akshay sunil masram
4+
* Internal callback used by the adapter to notify selection count changes.
55
*/
66
public interface NotifyItemChecked {
77
void notifyCheckBoxIsClicked();
8-
}
8+
}

library/src/main/java/com/developer/filepicker/controller/adapters/FileListAdapter.java

Lines changed: 102 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.content.Context;
44
import android.os.Build;
5-
import android.util.Log;
65
import android.view.LayoutInflater;
76
import android.view.View;
87
import android.view.ViewGroup;
@@ -19,29 +18,25 @@
1918
import com.developer.filepicker.model.FileListItem;
2019
import com.developer.filepicker.model.MarkedItemList;
2120
import com.developer.filepicker.widget.MaterialCheckbox;
22-
import com.developer.filepicker.widget.OnCheckedChangeListener;
2321

2422
import java.text.DateFormat;
2523
import java.util.ArrayList;
2624
import java.util.Date;
2725

2826
/**
29-
* @author akshay sunil masram
27+
* Adapter that renders files/folders and keeps checkbox state stable during row recycling.
3028
*/
3129
public class FileListAdapter extends BaseAdapter {
3230

33-
private static final String TAG = FileListAdapter.class.getSimpleName();
34-
35-
private ArrayList<FileListItem> listItem;
36-
private Context context;
37-
private DialogProperties properties;
31+
private final ArrayList<FileListItem> listItem;
32+
private final Context context;
33+
private final DialogProperties properties;
3834
private NotifyItemChecked notifyItemChecked;
3935

40-
public FileListAdapter(ArrayList<FileListItem> listItem, Context context,
41-
DialogProperties properties) {
42-
this.listItem = listItem;
36+
public FileListAdapter(ArrayList<FileListItem> listItem, Context context, DialogProperties properties) {
37+
this.listItem = listItem == null ? new ArrayList<>() : listItem;
4338
this.context = context;
44-
this.properties = properties;
39+
this.properties = properties == null ? new DialogProperties() : properties;
4540
}
4641

4742
@Override
@@ -50,120 +45,129 @@ public int getCount() {
5045
}
5146

5247
@Override
53-
public FileListItem getItem(int i) {
54-
return listItem.get(i);
48+
public FileListItem getItem(int position) {
49+
return listItem.get(position);
5550
}
5651

5752
@Override
58-
public long getItemId(int i) {
59-
return i;
53+
public long getItemId(int position) {
54+
return position;
6055
}
6156

6257
@Override
63-
public View getView(final int i, View view, ViewGroup viewGroup) {
58+
public View getView(final int position, View convertView, ViewGroup parent) {
6459
final ViewHolder holder;
65-
if (view == null) {
66-
view = LayoutInflater.from(context).inflate(R.layout.dialog_file_list_item,
67-
viewGroup, false);
68-
holder = new ViewHolder(view);
69-
view.setTag(holder);
60+
if (convertView == null) {
61+
convertView = LayoutInflater.from(context).inflate(R.layout.dialog_file_list_item, parent, false);
62+
holder = new ViewHolder(convertView);
63+
convertView.setTag(holder);
7064
} else {
71-
holder = (ViewHolder) view.getTag();
65+
holder = (ViewHolder) convertView.getTag();
7266
}
73-
final FileListItem item = listItem.get(i);
74-
if (MarkedItemList.hasItem(item.getLocation())) {
75-
Animation animation = AnimationUtils.loadAnimation(context,
76-
R.anim.marked_item_animation);
77-
view.setAnimation(animation);
78-
} else {
79-
Animation animation = AnimationUtils.loadAnimation(context,
80-
R.anim.unmarked_item_animation);
81-
view.setAnimation(animation);
67+
68+
final FileListItem item = listItem.get(position);
69+
final boolean isParentRow = isParentRow(position, item);
70+
final boolean isSelectable = isSelectable(item, isParentRow);
71+
72+
applySelectionAnimation(convertView, item);
73+
bindIcon(holder, item);
74+
bindTexts(holder, item, isParentRow);
75+
76+
holder.checkbox.setOnCheckedChangedListener(null);
77+
holder.checkbox.setVisibility(isSelectable ? View.VISIBLE : View.INVISIBLE);
78+
holder.checkbox.setChecked(MarkedItemList.hasItem(item.getLocation()));
79+
holder.checkbox.setEnabled(isSelectable);
80+
81+
if (isSelectable) {
82+
holder.checkbox.setOnCheckedChangedListener((checkbox, isChecked) -> {
83+
item.setMarked(isChecked);
84+
if (isChecked) {
85+
if (properties.selection_mode == DialogConfigs.MULTI_MODE) {
86+
MarkedItemList.addSelectedItem(item);
87+
} else {
88+
MarkedItemList.addSingleFile(item);
89+
}
90+
} else {
91+
MarkedItemList.removeSelectedItem(item.getLocation());
92+
}
93+
if (notifyItemChecked != null) {
94+
notifyItemChecked.notifyCheckBoxIsClicked();
95+
}
96+
});
8297
}
98+
99+
return convertView;
100+
}
101+
102+
private void applySelectionAnimation(View view, FileListItem item) {
103+
int animationRes = MarkedItemList.hasItem(item.getLocation())
104+
? R.anim.marked_item_animation
105+
: R.anim.unmarked_item_animation;
106+
Animation animation = AnimationUtils.loadAnimation(context, animationRes);
107+
view.startAnimation(animation);
108+
}
109+
110+
private void bindIcon(ViewHolder holder, FileListItem item) {
83111
if (item.isDirectory()) {
84-
holder.type_icon.setImageResource(R.mipmap.ic_type_folder);
85-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
86-
holder.type_icon.setColorFilter(context.getResources()
87-
.getColor(R.color.colorPrimary, context.getTheme()));
88-
} else {
89-
holder.type_icon.setColorFilter(context.getResources()
90-
.getColor(R.color.colorPrimary));
91-
}
92-
if (properties.selection_type == DialogConfigs.FILE_SELECT) {
93-
holder.checkbox.setVisibility(View.INVISIBLE);
94-
} else {
95-
holder.checkbox.setVisibility(View.VISIBLE);
96-
}
112+
holder.typeIcon.setImageResource(R.mipmap.ic_type_folder);
113+
holder.typeIcon.setColorFilter(getColorCompat(R.color.colorPrimary));
97114
} else {
98-
holder.type_icon.setImageResource(R.mipmap.ic_type_file);
99-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
100-
holder.type_icon.setColorFilter(context.getResources()
101-
.getColor(R.color.colorAccent, context.getTheme()));
102-
} else {
103-
holder.type_icon.setColorFilter(context.getResources()
104-
.getColor(R.color.colorAccent));
105-
}
106-
if (properties.selection_type == DialogConfigs.DIR_SELECT) {
107-
holder.checkbox.setVisibility(View.INVISIBLE);
108-
} else {
109-
holder.checkbox.setVisibility(View.VISIBLE);
110-
}
115+
holder.typeIcon.setImageResource(R.mipmap.ic_type_file);
116+
holder.typeIcon.setColorFilter(getColorCompat(R.color.colorAccent));
111117
}
112-
holder.type_icon.setContentDescription(item.getFilename());
118+
holder.typeIcon.setContentDescription(item.getFilename());
119+
}
120+
121+
private void bindTexts(ViewHolder holder, FileListItem item, boolean isParentRow) {
113122
holder.name.setText(item.getFilename());
114-
DateFormat dateFormatter = android.text.format.DateFormat.getMediumDateFormat(context);
115-
DateFormat timeFormatter = android.text.format.DateFormat.getTimeFormat(context);
116-
Date date = new Date(item.getTime());
117-
if (i == 0 && item.getFilename().startsWith(context.getString(R.string.label_parent_dir))) {
123+
if (isParentRow) {
118124
holder.type.setText(R.string.label_parent_directory);
119125
} else {
126+
Date date = new Date(item.getTime());
127+
DateFormat dateFormatter = android.text.format.DateFormat.getMediumDateFormat(context);
128+
DateFormat timeFormatter = android.text.format.DateFormat.getTimeFormat(context);
120129
holder.type.setText(String.format(context.getString(R.string.last_edit),
121130
dateFormatter.format(date), timeFormatter.format(date)));
122131
}
123-
if (holder.checkbox.getVisibility() == View.VISIBLE) {
124-
if (i == 0 && item.getFilename().startsWith(context.getString(R.string.label_parent_dir))) {
125-
holder.checkbox.setVisibility(View.INVISIBLE);
126-
}
127-
if (MarkedItemList.hasItem(item.getLocation())) {
128-
holder.checkbox.setChecked(true);
129-
} else {
130-
holder.checkbox.setChecked(false);
131-
}
132+
}
133+
134+
private boolean isSelectable(FileListItem item, boolean isParentRow) {
135+
if (item == null || isParentRow) {
136+
return false;
137+
}
138+
if (item.isDirectory()) {
139+
return properties.selection_type != DialogConfigs.FILE_SELECT;
132140
}
141+
return properties.selection_type != DialogConfigs.DIR_SELECT;
142+
}
133143

134-
holder.checkbox.setOnCheckedChangedListener(new OnCheckedChangeListener() {
135-
@Override
136-
public void onCheckedChanged(MaterialCheckbox checkbox, boolean isChecked) {
137-
item.setMarked(isChecked);
138-
if (item.isMarked()) {
139-
if (properties.selection_mode == DialogConfigs.MULTI_MODE) {
140-
MarkedItemList.addSelectedItem(item);
141-
} else {
142-
MarkedItemList.addSingleFile(item);
143-
}
144-
} else {
145-
MarkedItemList.removeSelectedItem(item.getLocation());
146-
}
147-
notifyItemChecked.notifyCheckBoxIsClicked();
148-
}
149-
});
150-
return view;
144+
private boolean isParentRow(int position, FileListItem item) {
145+
return position == 0 && item != null
146+
&& item.getFilename().startsWith(context.getString(R.string.label_parent_dir));
147+
}
148+
149+
private int getColorCompat(int colorRes) {
150+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
151+
return context.getResources().getColor(colorRes, context.getTheme());
152+
}
153+
return context.getResources().getColor(colorRes);
154+
}
155+
156+
public void setNotifyItemCheckedListener(NotifyItemChecked notifyItemChecked) {
157+
this.notifyItemChecked = notifyItemChecked;
151158
}
152159

153-
private class ViewHolder {
154-
ImageView type_icon;
155-
TextView name, type;
156-
MaterialCheckbox checkbox;
160+
private static class ViewHolder {
161+
final ImageView typeIcon;
162+
final TextView name;
163+
final TextView type;
164+
final MaterialCheckbox checkbox;
157165

158166
ViewHolder(View itemView) {
159167
name = itemView.findViewById(R.id.fname);
160168
type = itemView.findViewById(R.id.ftype);
161-
type_icon = itemView.findViewById(R.id.image_type);
169+
typeIcon = itemView.findViewById(R.id.image_type);
162170
checkbox = itemView.findViewById(R.id.file_mark);
163171
}
164172
}
165-
166-
public void setNotifyItemCheckedListener(NotifyItemChecked notifyItemChecked) {
167-
this.notifyItemChecked = notifyItemChecked;
168-
}
169173
}

0 commit comments

Comments
 (0)