Skip to content

Commit 7c678f2

Browse files
authored
Merge pull request #201 from tobexyz/feat/ui
Feat/UI
2 parents 9252a65 + bd1983b commit 7c678f2

32 files changed

Lines changed: 1162 additions & 1132 deletions

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ allprojects {
1515
google()
1616
mavenLocal()
1717
mavenCentral()
18+
maven { url 'https://jitpack.io' }
1819
}
1920

2021

yaacc/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ dependencies {
4141
}
4242
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
4343
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
44+
implementation 'androidx.lifecycle:lifecycle-runtime:2.8.0'
45+
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.8.0'
46+
implementation 'androidx.lifecycle:lifecycle-livedata:2.8.0'
47+
implementation 'androidx.viewpager2:viewpager2:1.1.0'
4448
implementation 'org.mp4parser:isoparser:1.9.56'
49+
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
4550

4651
}
4752

yaacc/src/main/java/de/yaacc/browser/BrowseContentItemAdapter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,13 @@ public void onBindViewHolder(final BrowseContentItemAdapter.ViewHolder holder, f
222222
asyncTasks.add(iconDownloadTask);
223223

224224
holder.playAll.setOnClickListener((v) -> {
225-
new ContentItemPlayTask(contentListFragment, currentObject).execute(ContentItemPlayTask.PLAY_ALL);
225+
new ContentItemPlayTask(contentListFragment, currentObject, null).execute(ContentItemPlayTask.PLAY_ALL);
226226
});
227227
holder.play.setOnClickListener((v) -> {
228-
new ContentItemPlayTask(contentListFragment, currentObject).execute(ContentItemPlayTask.PLAY_CURRENT);
228+
new ContentItemPlayTask(contentListFragment, currentObject, null).execute(ContentItemPlayTask.PLAY_CURRENT);
229229
});
230230
holder.playlistAdd.setOnClickListener((v) -> {
231-
new ContentItemPlayTask(contentListFragment, currentObject).execute(ContentItemPlayTask.ADD_TO_PLAYLIST);
231+
new ContentItemPlayTask(contentListFragment, currentObject, null).execute(ContentItemPlayTask.ADD_TO_PLAYLIST);
232232
Toast toast = Toast.makeText(contentListFragment.getActivity(), R.string.add_to_playlist, Toast.LENGTH_SHORT);
233233
toast.show();
234234
});

yaacc/src/main/java/de/yaacc/browser/ContentItemPlayTask.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ public class ContentItemPlayTask extends AsyncTask<Integer, Void, Void> {
2828
public final static int ADD_TO_PLAYLIST = 2;
2929
private final ContentListFragment parent;
3030
private final DIDLObject currentObject;
31+
private final Runnable onComplete;
3132

32-
33-
public ContentItemPlayTask(ContentListFragment parent, DIDLObject currentObject) {
33+
public ContentItemPlayTask(ContentListFragment parent, DIDLObject currentObject, Runnable onComplete) {
3434
this.parent = parent;
3535
this.currentObject = currentObject;
36+
this.onComplete = onComplete;
3637
}
3738

3839
@Override
@@ -49,4 +50,11 @@ public Void doInBackground(Integer... integers) {
4950
}
5051
return null;
5152
}
53+
54+
@Override
55+
protected void onPostExecute(Void result) {
56+
if (onComplete != null) {
57+
onComplete.run();
58+
}
59+
}
5260
}

yaacc/src/main/java/de/yaacc/browser/ContentListClickListener.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ public void onClick(View itemView) {
7373
contentListFragment.populateItemList(true);
7474
} else if (currentObject instanceof Item) {
7575
PlayableItem playableItem = new PlayableItem((Item) currentObject, 0);
76-
ContentItemPlayTask task = new ContentItemPlayTask(contentListFragment, currentObject);
76+
ContentItemPlayTask task = new ContentItemPlayTask(contentListFragment, currentObject, () -> {
77+
// Hide loading indicator when done
78+
contentListFragment.hideLoading(position);
79+
});
80+
// Show loading indicator
81+
contentListFragment.showLoading(position);
7782
if (playableItem.getMimeType() != null && playableItem.getMimeType().startsWith("video")) {
7883
task.execute(ContentItemPlayTask.PLAY_CURRENT);
7984
} else {

yaacc/src/main/java/de/yaacc/browser/ContentListFragment.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,24 @@ public void populateItemList(boolean clear) {
286286
});
287287
}
288288

289+
public void showLoading(int position) {
290+
requireActivity().runOnUiThread(() -> {
291+
ProgressBar progressBar = requireView().findViewById(R.id.contentListProgressBar);
292+
if (progressBar != null) {
293+
progressBar.setVisibility(View.VISIBLE);
294+
}
295+
});
296+
}
297+
298+
public void hideLoading(int position) {
299+
requireActivity().runOnUiThread(() -> {
300+
ProgressBar progressBar = requireView().findViewById(R.id.contentListProgressBar);
301+
if (progressBar != null) {
302+
progressBar.setVisibility(View.GONE);
303+
}
304+
});
305+
}
306+
289307
private void clearItemList() {
290308
requireActivity().runOnUiThread(() -> {
291309
navigator = new Navigator();
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (C) 2026 Tobias Schoene www.yaacc.de
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 3
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*/
18+
package de.yaacc.imageviewer;
19+
20+
import android.graphics.Bitmap;
21+
import android.net.Uri;
22+
import android.os.Bundle;
23+
import android.view.LayoutInflater;
24+
import android.view.View;
25+
import android.view.ViewGroup;
26+
import android.widget.ImageView;
27+
import android.widget.ProgressBar;
28+
29+
import androidx.annotation.NonNull;
30+
import androidx.annotation.Nullable;
31+
import androidx.fragment.app.Fragment;
32+
import androidx.lifecycle.Lifecycle;
33+
import androidx.lifecycle.LifecycleEventObserver;
34+
35+
import de.yaacc.R;
36+
import de.yaacc.Yaacc;
37+
import de.yaacc.util.YaaccLogger;
38+
39+
/**
40+
* Fragment for displaying a single image in ViewPager2.
41+
*/
42+
public class ImageFragment extends Fragment {
43+
private static final String ARG_URI = "uri";
44+
private Uri uri;
45+
private ImageView imageView;
46+
private ProgressBar progressBar;
47+
48+
public static ImageFragment newInstance(Uri uri) {
49+
ImageFragment fragment = new ImageFragment();
50+
Bundle args = new Bundle();
51+
args.putParcelable(ARG_URI, uri);
52+
fragment.setArguments(args);
53+
return fragment;
54+
}
55+
56+
@Override
57+
public void onCreate(@Nullable Bundle savedInstanceState) {
58+
super.onCreate(savedInstanceState);
59+
if (getArguments() != null) {
60+
uri = getArguments().getParcelable(ARG_URI);
61+
}
62+
}
63+
64+
@Nullable
65+
@Override
66+
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
67+
@Nullable Bundle savedInstanceState) {
68+
View view = inflater.inflate(R.layout.fragment_image, container, false);
69+
imageView = view.findViewById(R.id.imageView);
70+
progressBar = view.findViewById(R.id.progressBar);
71+
72+
// Add click listener to toggle controls
73+
imageView.setOnClickListener(v -> {
74+
if (getActivity() instanceof ImageViewerActivity) {
75+
((ImageViewerActivity) getActivity()).toggleControlsFromFragment();
76+
}
77+
});
78+
79+
return view;
80+
}
81+
82+
@Override
83+
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
84+
super.onViewCreated(view, savedInstanceState);
85+
if (uri != null) {
86+
loadImage(uri);
87+
}
88+
}
89+
90+
public void loadImage(Uri imageUri) {
91+
if (imageView == null || imageUri == null) return;
92+
93+
YaaccLogger.d(getClass().getName(), "Loading image: " + imageUri);
94+
95+
// Show loading indicator
96+
if (progressBar != null) {
97+
progressBar.setVisibility(View.VISIBLE);
98+
}
99+
imageView.setImageBitmap(null);
100+
101+
// Use existing ContentLoadExecutor
102+
Yaacc app = (Yaacc) requireActivity().getApplication();
103+
app.getContentLoadExecutor().execute(() -> {
104+
Bitmap bitmap = loadBitmap(imageUri);
105+
if (getActivity() != null) {
106+
getActivity().runOnUiThread(() -> {
107+
// Hide loading indicator
108+
if (progressBar != null) {
109+
progressBar.setVisibility(View.GONE);
110+
}
111+
if (imageView != null && bitmap != null) {
112+
imageView.setImageBitmap(bitmap);
113+
} else if (imageView != null) {
114+
imageView.setImageResource(R.drawable.yaacc192_32);
115+
}
116+
});
117+
}
118+
});
119+
}
120+
121+
private Bitmap loadBitmap(Uri imageUri) {
122+
try {
123+
if (getActivity() != null) {
124+
String scheme = imageUri.getScheme();
125+
if ("http".equals(scheme) || "https".equals(scheme)) {
126+
// Download from HTTP
127+
java.net.URL url = new java.net.URL(imageUri.toString());
128+
return android.graphics.BitmapFactory.decodeStream(url.openStream());
129+
} else {
130+
// Local file via ContentResolver
131+
return android.graphics.BitmapFactory.decodeStream(
132+
getActivity().getContentResolver().openInputStream(imageUri));
133+
}
134+
}
135+
} catch (Exception e) {
136+
YaaccLogger.e(getClass().getName(), "Failed to load image: " + imageUri, e);
137+
}
138+
return null;
139+
}
140+
141+
public void setImageUri(Uri uri) {
142+
this.uri = uri;
143+
if (imageView != null) {
144+
loadImage(uri);
145+
}
146+
}
147+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (C) 2026 Tobias Schoene www.yaacc.de
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 3
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*/
18+
package de.yaacc.imageviewer;
19+
20+
import android.net.Uri;
21+
22+
import androidx.annotation.NonNull;
23+
import androidx.fragment.app.Fragment;
24+
import androidx.fragment.app.FragmentActivity;
25+
import androidx.viewpager2.adapter.FragmentStateAdapter;
26+
27+
import java.util.List;
28+
29+
import de.yaacc.util.YaaccLogger;
30+
31+
/**
32+
* ViewPager2 adapter for image slideshow.
33+
*/
34+
public class ImagePagerAdapter extends FragmentStateAdapter {
35+
private List<Uri> imageUris;
36+
37+
public ImagePagerAdapter(@NonNull FragmentActivity fragmentActivity) {
38+
super(fragmentActivity);
39+
}
40+
41+
public void setImageUris(List<Uri> uris) {
42+
this.imageUris = uris;
43+
// Don't call notifyDataSetChanged() - ViewPager2 handles updates
44+
}
45+
46+
@NonNull
47+
@Override
48+
public Fragment createFragment(int position) {
49+
if (imageUris != null && position < imageUris.size()) {
50+
return ImageFragment.newInstance(imageUris.get(position));
51+
}
52+
return new ImageFragment();
53+
}
54+
55+
@Override
56+
public int getItemCount() {
57+
return imageUris != null ? imageUris.size() : 0;
58+
}
59+
60+
public Uri getItem(int position) {
61+
if (imageUris != null && position < imageUris.size()) {
62+
return imageUris.get(position);
63+
}
64+
return null;
65+
}
66+
}

0 commit comments

Comments
 (0)