how to print a layout with Print Document Adapter in android - java

i want to preview a layout for printer and want to print that layout. i already searched a lot but didn't find any way. i dont want to print many pages of document i just want to print one page one layout.
the layout contains text information, one list view and one imageView of QR code.
I have tried the following code:
private void doPrint() {
// Get a PrintManager instance
PrintManager printManager = (PrintManager) getApplicationContext()
.getSystemService(Context.PRINT_SERVICE);
// Set job name, which will be displayed in the print queue
String jobName = getApplicationContext().getString(R.string.app_name) + " Document";
// Start a print job, passing in a PrintDocumentAdapter implementation
// to handle the generation of a print document
printManager.print(jobName, new MyPrintDocumentAdapter(getApplicationContext()),
null);
}
and this is my Adapter class
package com.example.haier.arksolsfollow;
import android.content.Context;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.widget.Toast;
class MyPrintDocumentAdapter extends PrintDocumentAdapter {
public MyPrintDocumentAdapter(Context applicationContext) {
}
#Override
public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras) {
computePageCount(oldAttributes);
}
#Override
public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) {
}
#Override
public void onStart() {
super.onStart();
}
#Override
public void onFinish() {
super.onFinish();
}
private int computePageCount(PrintAttributes printAttributes) {
int itemsPerPage = 4; // default item count for portrait mode
// default item count for portrait mode
PrintAttributes.MediaSize pageSize = printAttributes.getMediaSize();
if (!pageSize.isPortrait()) {
// Six items per page in landscape orientation
itemsPerPage = 6;
}
// Determine number of print items
int printItemCount = 1;
return (int) Math.ceil(printItemCount / itemsPerPage);
}
}
my app also get crashes in this line
printManager.print(jobName, new MyPrintDocumentAdapter(getApplicationContext()),
null);
kindly guide me how to use all call back methods in adapter and how to preview a layout for printer

If i understood correctly you simply want to take a printout of your current layout. Assuming that i can provide you with an alternative solution. What you can do is first generate a bitmap of the layout you want to print and then pass it to the Printer Helper. You can even preview the bitmap before printing by setting it to an ImageView.
You can convert the layout view directly to a bitmap using below code.
View v = THE_LAYOUT_TO_BE_PRINTED;
Bitmap bmp = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmp);
v.draw(c);
Then you can print the bitmap using Print Helper
PrintHelper photoPrinter = new PrintHelper(getActivity());
photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);
photoPrinter.printBitmap("layout.png", bmp);
Below information is as for the Android Developer Documentation.
After the printBitmap() method is called, no further action from your
application is required. The Android print user interface appears,
allowing the user to select a printer and printing options. The user
can then print the image or cancel the action. If the user chooses to
print the image, a print job is created and a printing notification
appears in the system bar.
Edit:
Above method can be used to get a print out of an simple image without using the PrintDocumentAdapter itself. But if someone still prefers to use a custom PrintDocumentAdapter as in the original question, well constructed following article in Github will give a clear idea.

Related

Glide loads images from firebase painfully slow using URLs

I'm trying to create a RecyclerView that is populated by ImageViews in each cell and each image corresponds to an image in Firebase Storage.
I have a list of Strings that is passed into my RecyclerView adapter and each one represents a URL to an image in Firebase Storage. I load each image inside the onBindViewHolder().
What i get in return is a very VERY slow loading of a few images (around 5-see picture) and then it takes around 4 minutes to load another 5 and it never seems to load any other images after these.
I've read multiple posts on StackOverflow but most of them just tell you to use fitCenter() or centerCrop() but that doesn't change anything in my case. I also read in Glide's documentation that Glide will automatically downsample your images so i shouldn't need to do it manually, right? Any ideas what i could be doing wrong here? The Url Strings are successfully retrieved from Firebase and the queries are resolved almost instantly so i don't think there is any issue there.
UPDATE:
I've made some modifications in the onBindViewHolder() method in order to explicitly request caching of the images from Glide and i also used the thumbnail API to download lower resolutions of the images. Now more images are loading but each one still takes around 7 seconds to load which obviously is too long. If you have any suggestions let me know please.
Here's how the RecyclerView is set up in my main activity:
iconsRCV = findViewById(R.id.cardIconsRCV)
iconsRCV.layoutManager = GridLayoutManager(this,5) // set the layout manager for the rcv
val iconUrls : ArrayList<String> = ArrayList() // initialize the data with an empty array list
val adapter = CardIconAdapter(this,iconUrls) // initialize the adapter for the recyclerview
iconsRCV.adapter = adapter // set the adapter
Note that i get new data when certain queries are done and then i call adapter.notifyDataSetChanged() to pass new data to the RecyclerView.
CardIconAdapter.java:
public class CardIconAdapter extends RecyclerView.Adapter<CardIconAdapter.ViewHolder> {
private RequestOptions requestOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).centerCrop().error(R.drawable.applogotmp);
private List<String> urlsList;
private Context context;
class ViewHolder extends RecyclerView.ViewHolder {
ImageView iconImg;
ViewHolder(#NonNull View view) {
super(view);
iconImg = view.findViewById(R.id.cardIcon);
}
}
public CardIconAdapter(Context cntxt, List<String> data) {
context = cntxt;
urlsList = data;
}
#NonNull
#Override
public CardIconAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_icons_rcv_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CardIconAdapter.ViewHolder holder, int position) {
GlideApp.with(context).load(urlsList.get(position)).apply(requestOptions).into(holder.iconImg);
}
#Override
public int getItemCount() {
return urlsList.size();
}
}
P.S. The image sizes in Firebase are mostly udner 200KB but with a small few reaching 4MB. Also, the ImageView in the R.layout.card_icons_rcv_item layout is 75x75 in size.
Hope you have used latest version of glide.
There are few ways for better image loading and caching,
credit goes to this nice article .
1. Enable Disk Cache
val requestOptions = RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL)
Glide.with(context).load(url).apply(requestOptions).into(imageView)
2. List item
val requestOptions = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.signature(ObjectKey(signature))
Glide.with(context).load(url).apply(requestOptions).into(imageView)
3. Override Image Size (Optional)
val requestOptions = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.signature(ObjectKey(signature))
.override(100, 100) // resize does not respect aspect ratio
Glide.with(context).load(url).apply(requestOptions).into(imageView)
4. Add Thumbnail Url
// With thumbnail url
Glide.with(context).load(url)
.thumbnail(Glide.with(context).load(thumbUrl))
.apply(requestOptions).into(imageView)
// Without thumbnail url
// If you know thumbnail size
Glide.with(context).load(url)
.thumbnail(Glide.with(context).load(url).apply(RequestOptions().override(thumbSize)))
.apply(requestOptions).into(imageView)
// With size multiplier
Glide.with(context).load(url)
.thumbnail(0.25f)
.apply(requestOptions).into(imageView)
5. Setup Monthly Schedule for Cleaning
// This method must be called on the main thread.
Glide.get(context).clearMemory()
Thread(Runnable {
// This method must be called on a background thread.
Glide.get(context).clearDiskCache()
}).start()
6. To Transform bitmap
// TODO remove after transformation is done
.diskCacheStrategy(SOURCE) // override default RESULT cache and apply transform always
.skipMemoryCache(true) // do not reuse the transformed result while running
.diskCacheStrategy(DiskCacheStrategy.ALL) // It will cache your image after loaded for first time
.format(DecodeFormat.PREFER_ARGB_8888) //for better image quality
.dontTransform() // to load image faster just skip transform
.placeholder(R.drawable.placeholder) // use place holder while image is being load

onSaveInstanceState for ImageVeiw

I'm making a dice game and I want to save the image View when I rotate the screen.
I have tried to save the value and then re-establish it. This worked but when I wanted to download the picture but the value I saved it does not work.
// How do I save this?
public void setDiceOne() {
getDiceOne = new DiceAll(diceOne);
getDiceOne.getRandomNumber();
getDiceOne.getImageWhite();
}
public void setDiceTwo() {
getDiceTwo = new DiceAll(diceTwo);
getDiceTwo.getRandomNumber();
getDiceTwo.getImageWhite();
}

Load media thumbnails efficiently

I've spent few days to solve this problem but still can't find a solution. I'm new to Android so my code might be pretty messy!
I have a RecyclerView(Grid layout) that displays thumbnails for images and videos. It loads media files in a specific folder. But when I launch this activity, it takes up so much memory!
To load thumbnails, I created two threads.
Thread 1) MediaLoadThread that queries media files in SDCard. It loops through the cursor and queue thumbnail decode tasks to the different thread.
Thread 2) ThumbnailLoaderThread that decode each individual thumbnail. It receives the content resolver, media type(image or video), and media id. It uses basic .getThumbnail() method. After it's done with getting thumbnail, it triggers the response callback to it's caller thread(MediaLoadThread).
3) When MediaLoadThread(Thread 1) receives the callback, it triggers another callback that lets the activity update the adapter item of the given position. The adapter updates the UI and finally the thumbnail ImageView changes from placeholder to actual thumbnail.
:::Here's my code:::
1) MediaLoadThread.java
#Override
public void run() {
mMediaDataArr.clear();
mLoaderThread.start(); // Prepping the thread 2
mLoaderThread.prepareHandler();
// .... SD Card query stuff .....
if (mediaCursor != null && mediaCursor.moveToFirst()) {
do {
mMediaDataArr.add(new MediaData(videoCursor.getInt(columnIndexId),
mediaCursor.getLong(columnIndexDate), //ID
mediaCursor.getInt(columnIndexType), //MEDIA TYPE
null); //THUMBNAIL BITMAP (NULL UNTIL THE ACTUAL THUMBNAIL IS DECODED)
} while (mediaCursor.moveToNext());
mediaCursor.close();
mResponseHandler.post(new Runnable() {
#Override
public void run() {
// This callback lets the activity activate the adapter and recyclerview so that the user can interact with recyclerview before the app finishes decoding thumbnails.
mCallback.onVideoLoaded(mMediaDataArr);
}
});
//Passing tasks to thread 2
for (int i = 0; i < mMediaDataArr.size(); i++) {
mLoaderThread.queueTask(
mMediaDataArr.get(i).getmMediaType(),
i, mMediaDataArr.get(i).getmMediaId());
}
}
}
}
// This is triggered by thread 2 when it finishes decoding
#Override
public void onThumbnailLoaded(final int position, final Bitmap thumbnail) {
mResponseHandler.post(new Runnable() {
#Override
public void run() {
mCallback.onThumbnailPrepared(position, thumbnail);
}
});
}
2) ThumbnailLoaderThread.java
public void queueTask(int mediaType, int position, int videoId) {
mWorkerHandler.obtainMessage(mediaType, position, videoId).sendToTarget();
}
public void prepareHandler() {
mWorkerHandler = new Handler(getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
int type = msg.what;
final int position = msg.arg1;
int videoId = msg.arg2;
try {
if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) {
Bitmap newThumb = MediaStore.Images.Thumbnails
.getThumbnail(mCr, videoId,
MediaStore.Images.Thumbnails.MINI_KIND, null);
postResult(position, newThumb);
} else if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
Bitmap newThumb = MediaStore.Video.Thumbnails
.getThumbnail(mCr, videoId,
MediaStore.Video.Thumbnails.MINI_KIND, null);
postResult(position, newThumb);
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
});
}
private void postResult(final int position, final Bitmap newThumb) {
mResponseHandler.post(new Runnable() {
#Override
public void run() {
mCallback.onThumbnailLoaded(position, newThumb);
}
});
}
3) LibraryActivity.java
#Override
public void onThumbnailPrepared(int position, Bitmap thumbnail) {
if (thumbnail != null && position < mData.size()) {
MediaData updatedData = mData.get(position);
updatedData.setmThumbnail(thumbnail);
mData.set(position, updatedData);
mVideoAdapter.notifyItemChanged(position);
}
}
The flow is like this.
1) The activity starts the thread 1.
2) Thread 1 starts querying files and starts thread 2. It passes the media id looping through the cursor.
3) Thread 2 decodes thumbnails with the given media id.
4) When decoding is done, thread 2 triggers the callback to Thread 1 with the result bitmap.
5) Thread 1 receives the bitmap and delivers the bitmap to activity through callback.
6) Activity receives the thumbnail and updates the RecyclerView data with the given bitmap.
It works fine, but when the system allocates almost 50MB of memory for this task... Considering it was only loading 100 thumbnails, I think it's pretty heavy.
:::What I've tried:::
1) I extracted the URI of each individual thumbnail and let the recyclerview adapter to load the image with the given URI when it binds. It works fine and did not consume that much memory, but because it loads images when the item is bound, it reloads the thumbnail whenever I scroll the screen with a little bit of delay.
2) I let the adapter to load thumbnails with the direct thumbnail path. But it won't work when the user cleans up the /.thumbnails folder.
3) I set the BitmapFactory.Options samplsize into 4 when the thread decodes thumbnails. But when it was still heavy and even slower sometimes...
4) In MediaData object, it holds the thumbnail bitmap as a member variable. So I made it null right after the adapter loaded it into the ImageView. Still heavy, and because I made the object's thumbnail into null, it just shows the placeholder when I scroll back.
I really have no clue. Any help would be appreciated!!
You can used nostra universal image loader library to load images. This library is very good for image loading and also some other library like Picasso, glide etc available which you can used instead of making manual coding.

Finding Screen Dimensions Android

I have read a few posts on this site about finding screen dimensions and most of them reference getWindowManager().getDefaultDisplay;
I tried to this is my code and got an error saying cannot resolve method getWindowManager(). My code is below so you can fully understand:
public class MainActivity {
public static void main(String args[])throws IOException{
Display display = getWindowManager().getDefaultDisplay;
//WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
//int displayid = Display.DEFAULT_DISPLAY;
//Display display = DisplayManager.getDisplay(displayid);
int tlx = (168/800)*display.getWidth();
int tly = (136/480)*display.getHeight();
int brx = (631/800)*display.getWidth();
int bry = (343/480)*display.getHeight();
int rotation = display.getRotation();
This is just an excerpt in case somethings don't make sense. Also, I'm using the display dimensions to find screen elements within an application. If I should be using the window dimensions for this please inform me upon how to go about doing that as well, but I still need to get the display right so I can use methods such as the .getRotation() one shown above. Thanks in advance for your help.
You need to reference getWindowManager from an Activity. Your MainActivity doesn't appear to be extending Activity so you either need to extend Actvity
public class MainActivity extends Activity {
Or pass an activity to that class then do something like this.
Display display = myActivity.getWindowManager().getDefaultDisplay();
Or through a Context
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

MediaStore.Images.Thumbnails.getThumbnail returns wrong thumbnail instead of NULL

Consider the scenario as in this picture:
Three photos, one of them is a large GIF file (3MP).
I'm querying MediaStore in order to retrieve the correspondent thumbnails. If I initialize the Cursor via CursorLoader with this sortOrder:
MediaStore.Images.Media.DATE_ADDED + " DESC""
What happens: MediaStore returns the previous successfully retrieved thumbnail:
Expected behaviour: when MediaStore cannot retrieve the thumbnail of a given image for some reason it has to return NULL, as per its Javadoc: "... Returns A Bitmap instance. It could be null if the original image associated with origId doesn't exist or memory is not enough."
If I initialize the Cursor with this sortOrder:
MediaStore.Images.Media.DATE_ADDED + " ASC""
It runs just fine:
However I can't simply change the sortOrder since the requirement is to show the newest pictures first.
Below is my sample code and here is the complete sample project as well as the three images used to reproduce.
package com.example.getimagefrommediastore;
import android.app.Activity;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader;
import android.widget.ImageView;
import android.widget.TextView;
public class GetThumbnailsFromMediaStoreSampleActivity extends Activity {
TextView mThumb_id_01;
TextView mThumb_id_02;
TextView mThumb_id_03;
ImageView mImg_01;
ImageView mImg_02;
ImageView mImg_03;
boolean isThumb01 = true; // Simple flag to control this example
boolean isThumb02 = true;
Cursor mCursorLoader;
int mColumnIndex;
long mOrigId; // Original image id associated with thumbnail of interest
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Just initializing views
mThumb_id_01 = (TextView) findViewById(R.id.thumb_id_01);
mThumb_id_02 = (TextView) findViewById(R.id.thumb_id_02);
mThumb_id_03 = (TextView) findViewById(R.id.thumb_id_03);
mImg_01 = (ImageView) findViewById(R.id.thumb_01);
mImg_02 = (ImageView) findViewById(R.id.thumb_02);
mImg_03 = (ImageView) findViewById(R.id.thumb_03);
// Initializing CursorLoader
mCursorLoader = initializeCursorLoader();
mColumnIndex = mCursorLoader.getColumnIndex(MediaStore.Images.Media._ID);
// Go thru all the images in the device (EXTERNAL_CONTENT_URI)
// In this example there are only three images
for (int i = 0; i < mCursorLoader.getCount(); i++) {
mCursorLoader.moveToPosition(i);
mOrigId = mCursorLoader.getInt(mColumnIndex);
// Update views
chooseViewToUpdate();
}
}
private Cursor initializeCursorLoader() {
String[] COLUMNS = {
MediaStore.Images.Thumbnails._ID, MediaStore.Images.Media.DATA
};
CursorLoader cursorLoader = new CursorLoader(
GetThumbnailsFromMediaStoreSampleActivity.this, // Context
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, // Uri
COLUMNS, // Projection
null, // Selection
null, // Selection Args
// Sort Order: DESC = newest first
// Sort Order: ASC = oldest first
MediaStore.Images.Media.DATE_ADDED + " DESC");
// *** NOTE ***
// With:
//
// MediaStore.Images.Media.DATE_ADDED + " ASC"
//
// It runs just fine (MediaStore returns 'null' for invalid thumbnails)
// The problem seems to reside on the " DESC" tag.
//
// How bizarre is that?
return cursorLoader.loadInBackground();
}
private void chooseViewToUpdate() {
if (isThumb01) {
updateUI(mThumb_id_01, mImg_01);
isThumb01 = false;
} else if (isThumb02) {
updateUI(mThumb_id_02, mImg_02);
isThumb02 = false;
} else {
updateUI(mThumb_id_03, mImg_03);
}
}
private void updateUI(TextView textView, ImageView imgView) {
textView.setText("ID:" + String.valueOf(mOrigId));
Bitmap mediaStoreThumbmail = MediaStore.Images.Thumbnails.getThumbnail(
this.getContentResolver(),
mOrigId,
MediaStore.Images.Thumbnails.MICRO_KIND, null);
if (mediaStoreThumbmail != null) {
imgView.setImageBitmap(mediaStoreThumbmail);
}
}
Am I missing something? Does anyone have an idea what may be wrong?
I filled a bug against Android anyway.
EDIT
It seems that this issue is fixed in Lollipop. (The last comment on that thread).
I"m just guessing here. When you ask for the MICRO_KIND the os is creating a new image which gets next in line on DESC cursor producing the same image again.
One work around is to load an ArrayList for the image id's. Then to go after the thumbnails working from the ArrayList.
Possibility try your code using the MINI_KIND and bmoptions.inSampleSize = 2;
final BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inSampleSize =2;
Bitmap bm = MediaStore.Images.Thumbnails.getThumbnail(
context.getContentResolver(), newImageId,
MediaStore.Images.Thumbnails.MINI_KIND,
bmOptions);
After three years, it seems that this issue is fixed in Lollipop. (The last comment on that thread).
Therefore I'm answering my own question.

Categories

Resources