I am trying to write a simple application that loads images that are stored in a web directory I've created. I want the application to grab ALL of the images that are stored in said directory and display them in a layout.
The code below works for only certain type of URLs(mainly tinypic, but not photobucket, imageshack etc) and also not for directories.
I've my image names formatted in sequence like image1.png, image2.png, etc. so that the code snippet and the downloadFile()-method would work.
But, I'm not sure how I would grab all the images from the server simultaneously because this only grabs one image at a time, sets it as a bitmap and then displays it in an IV. I'm pretty new to Android so some help in the right direction would be appreciated.
public class NewWallpapersActivity extends Activity {
ImageView imView1;
String imageUrl="http://i133.photobucket.com/albums/q44/slimjady/Wallpapers";
Random r= new Random();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle SavedInstanceState) {
super.onCreate(SavedInstanceState);
setContentView(R.layout.newwallpapers);
Button bt3= (Button)findViewById(R.id.get_imagebt);
bt3.setOnClickListener(getImgListener);
imView1 = (ImageView)findViewById(R.id.IV1);
}
View.OnClickListener getImgListener = new View.OnClickListener(){
public void onClick(View view) {
//i tried to randomize the file download, in my server i put 4 files with name like
//png0.png, png1.png, png2.png so different file is downloaded in button press
int i =r.nextInt(4);
downloadFile(imageUrl+"png"+i+".png");
Log.i("im url",imageUrl+"png"+i+".png");
}
};
Bitmap bmImg;
void downloadFile(String fileUrl){
URL myFileUrl = null;
try {
myFileUrl= new URL(fileUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bmImg = BitmapFactory.decodeStream(is);
imView1.setImageBitmap(bmImg);
} catch (IOException e) {
String exception = "Erro: An Exception Was Thrown";
Toast errortoast = Toast.makeText(this, exception, Toast.LENGTH_SHORT);
errortoast.show();
e.printStackTrace();
}
}
}
Here are some thoughts on this one:
Displaying all Images
Since you want to display all Images from within a single folder, I would suggest you to have a Directory-Activity, which displayes all the images in the given directory and one Image-Activity which displays only one single Image.
To proper present this I would suggest using a GridView to display all the Images and a normal Activity with a full-screen ImageView to display the single Image.
You could also use the Gallery-widget to "combine" both those ideas.
Not letting the user wait
One of the bigger problems with your approach is, that the images are getting downloaded on the UI-Thread.
Depending on the size of all or on single image and the available connection-speed, Downloading the image can take a while. While the images are getting downloaded, the UI-Thread will wait for your downloadStuff()-method to return and the UI will freeze. This might create the illusion that your App just crashed.
So, you'll want to do the downloading in a separate thread. Android has a handy wrapper for this called an AsyncTask.
As a little bonus to show your user that the process might take a while you can use a ProgressDialog to illustrate this (on the UI-Thread).
Knowing whats in a directory
Now we come to the point where we have to face some limitations. If you want to use Picture-Hosters like the ones you listed above, you'll need to check if they offer you an API (or something equal) to get a list of Images (or their URLs) from a certain directory/album (look for a "API"-link on the front-page).
If you host the images on your own Server, you should be able to create a little PHP-script (or whatever script/programming language you prefer), which then lists all image-files in the directory and presents them in an easy-to-parse way (either JSON or XML).
This should enable you to get a list of URLs to the Image-files you want to display.
Related
I am working on stripe-terminal-android-app, to connect to BBPOS 2X Reader device,
wanted to click-item from list,(recyclerView).
I am trying to do:
when list of devices appears(readers), I am checking if readers.size()==1, then click first-device from list,else show recyclerView();
I have very less experience in Android(coming from JS, PY), :)
After going through debugger to understand flow of program-running, I used F8 key, or stepOver the functions one by one,
and where value is assigned to convert in displayble-format in adapter as here.
public ReaderAdapter(#NotNull DiscoveryViewModel viewModel) {
super();
this.viewModel = viewModel;
if (viewModel.readers.getValue() == null) {
readers = new ArrayList<>();
} else {
readers = viewModel.readers.getValue();
if(readers.size() == 1){
Log.e(TAG, "readers.size() is 1 "+ readers.size());
}
}
}
then in ReaderHolder-file, values are bind() as
void bind(#NotNull Reader reader) {
binding.setItem(reader);
binding.setHandler(clickListener);
binding.executePendingBindings();
}
}
I tried assigining button and manually clicking when only-one device appears, by clicing on reader[0], can't do that by findViewById inside Adapter file, to call onClick() method manually,
I tired another StackOverflow's answer but didn't understood, from here.
Main fragment is discovery-fragment,
how can I click first-device by checking readers.size()==1, then click onClick()?
my final-goal is to automate, whole stripe-terminal-payment process on android.
extra-info:
I am fetching data from python-odoo server, then using url, will open app through browser, (done this part), then device will be selected automatically as everytime-no any devices will be present except one,
so will automatically select that from recyclerView, then proceed.
I have asked for help in detailed way on GitHub-issues, and started learning Android's concepts for this app(by customizing stripe's demo app, which works great, but I wanted to avoid manually clicking/selection of devices).
there are simple activities A and B. Activity A launches Activity B and i can select an image as profile image, which first I will upload it to the server and then show the image via the received url. At first when I switch to activity B everything works fine, if there is an image already downloaded its shown in the imageview or it will be downloded and shown. in this case if I select other images to upload, its working just fine and the imageview will show the new images. when I exit the activity and relaunch it, the previously downloded image is shown (if any exists), but any new image uploaded to server won't be shown not even drawables. the imageview won't update at all until I exit the activity again and relaunch it, which in this case the previous image uploaded will be shown but again no new image is shown. so to sum it, first time everything works fine, but for the next times the image is loaded properly only one time.
I've been using Glide and Frisco and thought the problem is caused by them but then used simple ImageView and simple connection to download bitmap and it was the same. it works only one time.
I've been testing both on Nokia 6.1 plus Android 9 and Nexus 5x Android 8.1 and emulators with Android 8.1 and Android 9. I've also tried the release build and still the same.In addition, I'm using AndroidX and the strategy to download image and load them is the same as my previous projects and I'd never faced this issue before.
tried these below before and after loading image and it's not working.
with Glide and Frisco the cache is also disabled. and The urls are completely random so they wont even have the chance to cache them.
here is a sample download and load, its just temporary.
image.setImageResource(0);
image.setImageBitmap(null);
image.invalidate();
new Thread(new Runnable() {
#Override
public void run() {
Bitmap bmp = null;
try {
trustEveryone();
URL url = new URL(user.image_address);
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
Bitmap finalBmp = bmp;
ProfileActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
_profileImage.setImageBitmap(null);
_profileImage.setImageResource(0);
_profileImage.invalidate();
_profileImage.setImageBitmap(finalBmp);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
before using below method, tested it on my backup project before migrating to androidX and it was the same so the problem was not because of androidX
so here is the solution. the activities order : Main->Profile->Gallery->Crop->Back to Profile
to return the results from Crop to Profile I'm using event bus which has to be run on UI thread, so the Activity used to runOnUI... to post the event was CropActivity which was finished after the event is posted, and although I used ProfileActivity.runOnUI ... to load the image when receiving the event from event bus, the whole code was being run on CropActivity so I used another workaround to send results back to previous activity and that solved the problem without the need for invalidate or setting the image to null.
So I'm using Glide to load video thumbnails, but it takes a while to load a large number of videos to view, whats the best/fastest way to load a list of videos in users phone with each respective video thumbnail, in Recycler view.
Populating the list
public static ArrayList<String> getAllMedia(Context context) {
HashSet<String> videoItemHashSet = new HashSet<>();
String[] projection = { MediaStore.Video.VideoColumns.DATA ,MediaStore.Video.Media.DISPLAY_NAME};
Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
try {
cursor.moveToFirst();
do{
videoItemHashSet.add((cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))));
}while(cursor.moveToNext());
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
ArrayList<String> downloadedList = new ArrayList<>(videoItemHashSet);
return downloadedList;
}
The Glide Method
public static void displayImageOriginal(Context ctx, ImageView img, String url) {
try {
Glide.with(ctx).load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.apply(RequestOptions.centerCropTransform().skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.ALL))
.into(img);
} catch (Exception e) {
}
}
Here's the adapter view Binder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final YVideos obj = items.get(position);
if (holder instanceof OriginalViewHolder) {
OriginalViewHolder view = (OriginalViewHolder) holder;
view.video_title.setText(obj.title);
view.duration_size.setText(obj.getDurSize());
Tools.displayImageOriginal(ctx, view.video_thumbnail, obj.name);
} else {
SectionViewHolder view = (SectionViewHolder) holder;
view.title_section.setText(obj.title);
}
}
This all looks like standard RecyclerView usage, so provided your adapter is assigned to the RecyclerView in isolation, i.e. without definining any properties that limit performance, such as setItemViewCacheSize(), setMaxRecycledViews() and the likes, your issue is unlikely to be a client-side one.
Are you relying on Glide to generate the thumbnails for you, at runtime? If so, you'll be really putting the library through its paces with a lot of processing, since it has to work through the original quality, original resolution video on the fly. Would it not be possible to generate the thumbnails beforehand? These will load much faster and unburden your app from a lot of computation. If the thumbnails are going to stay the same forever, then why not compute them once and let every user benefit, rather than have every user compute the same result every time you wish to render?
It's likely to me that your issue comes from the fact that you're expecting to fetch the images and render them to the user whilst they're already looking at the content. This means that you load up the screen, and onBindViewHolder() is called, and you make an attempt to fetch the images from a network; but at this stage, the user is already looking at the list and ready to browse. In a way, you're a little too late, and your List will only be as fast as your network connection/image server.
What you can do is prepare the image cache before you ever try to draw the RecyclerView. You know before you reach that screen what range of images you wish to render; it would be possible to load these into Glide and define an appropriate diskStrategy before you're even ready to draw. This way, you can initialize the List of content the moment the user is looking. The predictive image caching strategy wouldn't end here; you know what rows of images you're ready to load next; so you can fetch those in the background too. The drawback with this approach is that it has the potential to waste a lot of bandwidth fetching images your user may never even scroll down to; therefore you may need to experiment with request rate limits.
There are also sneakier ways to go about solving this problem. Even the most well funded, well researched applications depend on simple tricks to work around the bottleneck of network performance... Have you considered using a placeholder animation to keep your app looking active? You'll be surprised how well these work!
i am creating an application for android in eclipse.
i have a chat application in which user's contact's images are downloaded in sdcard and are put in a HashMap (url, localAddress), when i want to load contact list, for any contacts I use a function to find address of contacts' pictures on sdcard, three different state may occur
1- image found on sdcard then return path.
2- image don't download before and HashMap return null, then download it.
3- HashMap return a path but user deleted image from sdcard then remove key from Hashmap and download again.
my code:
public static String findFile(Context ctx, String url, String type)
{
try
{
String value = globalVars.addresses.get(url);
if(value != null)
{
File ff = new File(value);
if(ff.exists())
return value;
globalVars.addresses.remove(url);
}
globalVars.enqueueJob(ctx, new globalVars.downloadJob(url, url, type));
return null;
}
catch(Exception ex)
{
Log.e("Find File", "Start");
Log.e("Find File", ex.toString());
ex.printStackTrace();
return null;
}
}
but when i delete an image it download more and more .
function enqueueJob :
public static void enqueueJob(Context context, downloadJob dj)
{
if(inQueue.get(dj.getAddress())!= null && inQueue.get(dj.getAddress())== true)
return;
downloads.add(dj);
inQueue.put(dj.address, true);
FileUtils.doDownload(context);
}
It work's fine for pictures don't download yet and pictures that download and don't delete yet.
Your title is confusing as it indicates you suspect the File.exists as the problem. Looking at your code, the fact that deleted images are added to download queue means File.exists works fine at least the first time. Otherwise, you would not even add it to the queue. Now the question is what happens when it is added to queue and downloaded. Check your code to make sure that it added back to the map properly. In your code, something should be added to the download queue only under two conditions: one if nothing in the map and the other is whatever in the map is deleted. So either it is not added back properly or may be a race condition where your File.exists is happening before the download is completed fully and you are adding it back to the queue again.
Good luck.
I am working on a small File manager to get the hang of things on Android and I have cut/copy/paste operation. It seems to work fine, but I run into issues with pasting things. On my app it is displayed fine, but for it to be recognized by other apps, it requires that I reboot the phone. For example, if I cut/paste an image from the download folder to the DCIM folder, the gallery app does not display that image unless I reboot the whole phone. I am using an algorithm that takes data byte by byte. Its extremely inefficient, but I am not totally sure how to implement a faster algorithm.
Thanks.
No actually you wont need to reboot our phone. When you copy an image and paste it to other location your newly copied media file is not added to the android's ContentResolver. So you should scan your data using the class MediaScannerConnection
Eg:
When you paste a file you have the file right ?
Modify as your wish, this works fine
private void scanImage(File targetLocation) {
// Scans the media to load images
String mimetype = Utility.getMimeType(targetLocation.getAbsolutePath());
if(mimetype.contains("image"))
{
MediaScannerConnection.scanFile(context, new String[] { targetLocation.getPath() }, new String[] { "image/jpeg" }, this);
}
}
UPDATE
Callback should be like
either you can implement OnScanCompletedListener in your class and add unimplemented method, So you can pass this as callback OR you can use
OnScanCompletedListener listener = new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
// you will get the callback here
}
};
and pass listener as callback