I implemented a custom Android camera using the camera2 api and when I click capture button I save the captured image to my phone eternal storage.
Now I needed to show a popup to enter the picture name before saving it and this popup has an imageView to show the captured image.
Even though I am loading the image from the Main thread I am getting this error
"Only the original thread that created a view hierarchy can touch its views"
Here is the code
private void initPopup(final Bitmap bitmap) {
popAddName = new Dialog(this);
popAddName.setContentView(R.layout.popup_add_name);
popAddName.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popAddName.getWindow().setLayout(Toolbar.LayoutParams.MATCH_PARENT, Toolbar.LayoutParams.WRAP_CONTENT);
popAddName.getWindow().getAttributes().gravity = Gravity.TOP;
popup_add = popAddName.findViewById(R.id.popup_add);
popup_img = popAddName.findViewById(R.id.popup_img);
popup_name = popAddName.findViewById(R.id.popup_name);
Thread thread = new Thread() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
Picasso.with(getApplicationContext()).load(getImageUri(getApplicationContext(), bitmap)).placeholder(R.drawable.placeholder).into(popup_img);
}
});
}
};
thread.start();
}
Here is the method to convert Bitmap to Uri for Picasso loading
public Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
This is the capture method
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader imageReader) {
Image image = null;
try {
image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
//CONVERTION BYTES[] TO BITMAP
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//create a new folder in external storage directory
String lorealFolder = "coffretPics";
File f = new File(Environment.getExternalStorageDirectory(), lorealFolder);
if (!f.exists()) {
f.mkdirs();
}
initPopup(bitmap);
popAddName.show();
file = new File(Environment.getExternalStorageDirectory() + "/" + lorealFolder + "/" + UUID.randomUUID().toString() + ".jpg");
save(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
{
if (image != null)
image.close();
}
}
}
private void save(byte[] bytes) throws IOException {
/* OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(file);
outputStream.write(bytes);
} finally {
if (outputStream != null) {
outputStream.close();
}
}*/
}
};
Can someone please tell me what's the problem ?
Updated answer:
Create a class called AppExecutors
public class AppExecutors {
// For Singleton instantiation
private static final Object LOCK = new Object();
private static AppExecutors sInstance;
private final Executor diskIO;
private final Executor mainThread;
private final Executor networkIO;
private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
this.diskIO = diskIO;
this.networkIO = networkIO;
this.mainThread = mainThread;
}
public static AppExecutors getInstance() {
if (sInstance == null) {
synchronized (LOCK) {
sInstance = new AppExecutors(Executors.newSingleThreadExecutor(),
Executors.newFixedThreadPool(3),
new MainThreadExecutor());
}
}
return sInstance;
}
public Executor diskIO() {
return diskIO;
}
public Executor mainThread() {
return mainThread;
}
public Executor networkIO() {
return networkIO;
}
private static class MainThreadExecutor implements Executor {
private Handler mainThreadHandler = new Handler(Looper.getMainLooper());
#Override
public void execute(#NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
And get the main thread like:
AppExecutors.getInstance().mainThread().execute(new Runnable() {
#Override
public void run() {
// Do the work on UI thread here.
}
});
That should fix your problem.
Related
I want to save image from url to storage through picasso, but i have a problem with my code.
i've a variable to save image.
String urlImage = "http://mylink/buzz/test2.jpg";
this one is worked
but when i use
fileUrl = fileName +"." + fileType;
String urlImage = "http://mylink/buzz/" + fileUrl;
not worked
content of fileUrl is test2.jpg
here is my code to save image
private Target picassoImageTarget(Context context, final String imageDir, final String imageName) {
Log.d("picassoImageTarget", " picassoImageTarget");
ContextWrapper cw = new ContextWrapper(context);
final File directory = cw.getDir(imageDir, Context.MODE_PRIVATE); // path to /data/data/yourapp/app_imageDir
return new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
final File myImageFile = new File(directory, imageName); // Create image file
FileOutputStream fos = null;
try {
fos = new FileOutputStream(myImageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Log.i("image", "image saved to >>>" + myImageFile.getAbsolutePath());
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
if (placeHolderDrawable != null) {}
}
};
}
and its code for call picasso.
Picasso.with(DrawerActivity.this).load(url).into(picassoImageTarget(getApplicationContext(), "imageDir", fileUrl));
I created an image gallery app.
My requirment: I want to select multiple images, click on button cut and come back to activity which displays all folders (ImageGallery.java). Now, I want to select a folder and paste all the selected images in that folder, on selecting the folder.
What is happening? I am able to select images using my app and come back to activity which displays all folders but not able to move them using my app.
I put the code for moving images in a background thread using task. I select images from one folder, come back to the activity which displays all the folders (ImageGallery.java) and select the folder to which the images are to be moved. But when I try to move images, selected images do not move to other folder being selected, on selecting a folder. I guess the code inside AsyncTask isn't even getting executed.
How do I fix it ?
PhotosActivity.java (Activity used to select images):
int int_position;
private GridView gridView;
GridViewAdapter adapter;
ArrayList<Model_images> al_menu = new ArrayList<>();
private ArrayList<Integer> mSelected = new ArrayList<>();
boolean boolean_folder;
gridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(final AdapterView<?> parent, View view, final int position, long id) {
if (mSelected.contains(position)) {
mSelected.remove(position);
view.setBackgroundColor(Color.TRANSPARENT);// remove item from list
// update view (v) state here
// eg: remove highlight
} else {
mSelected.add(position);
view.setBackgroundColor(Color.LTGRAY);// add item to list
// update view (v) state here
// eg: add highlight
}
buttoncut.setVisibility(View.VISIBLE);
button2.setVisibility(View.VISIBLE);
button3.setVisibility(View.VISIBLE);
button4.setVisibility(View.VISIBLE);
button5.setVisibility(View.VISIBLE);
buttoncut.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
buttoncut.setVisibility(View.GONE);
button2.setVisibility(View.GONE);
button3.setVisibility(View.GONE);
button4.setVisibility(View.GONE);
button5.setVisibility(View.GONE);
Intent moveIntent = new Intent(PhotosActivity.this, ImageGallery.class);
moveIntent.putIntegerArrayListExtra("selected_images", mSelected);
startActivity(moveIntent);
}
});
ImageGallery.java:
public static ArrayList<Model_images> al_images = new ArrayList<>();
ArrayList<Integer> selectedImages = new ArrayList<>();
boolean boolean_folder;
Adapter_PhotosFolder obj_adapter;
GridView gv_folder;
private static final int REQUEST_PERMISSIONS = 100;
int int_position;
selectedImages = getIntent().getIntegerArrayListExtra("selected_images");
if (selectedImages != null) {
Toast.makeText(ImageGallery.this, "This code gets executed", Toast.LENGTH_SHORT)
.show();
new LongOperation().execute();
}
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int image : selectedImages) {
File sourceImage = new File(al_images.get(int_position).getAl_imagepath().get(image)); //returns the image File from model class to be moved.
File destinationImage = new File(al_images.get(int_position).getStr_folder(), ".jpeg");
try {
copyOrMoveFile(sourceImage, destinationImage, true);
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
//Method to move the file
private void copyOrMoveFile(File file, File dir, boolean isCopy) throws IOException {
File newFile = new File(dir, file.getName());
FileChannel outChannel = null;
FileChannel inputChannel = null;
try {
outChannel = new FileOutputStream(newFile).getChannel();
inputChannel = new FileInputStream(file).getChannel();
inputChannel.transferTo(0, inputChannel.size(), outChannel);
inputChannel.close();
if (!isCopy)
file.delete();
} finally {
if (inputChannel != null) inputChannel.close();
if (outChannel != null) outChannel.close();
}
}
}
You have to use Intent.ACTION_MEDIA_SCANNER_SCAN_FILE for updating media store.
Inside AsyncTask -> onPostExecute method fetch latest images from MediaStore
private class LongOperation extends AsyncTask<String, Void, File> {
#Override
protected File doInBackground(String... params) {
for (String imagePath : selectedImages) {
File sourceImage = new File(imagePath); //returns the image File from model class to
// be// moved.
File destinationImage = new File(al_images.get(int_position).getDirectoryPath() +
File.separator + sourceImage.getName());
try {
moveFile(sourceImage, destinationImage, true);
return destinationImage;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPreExecute() {
}
#Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
getBaseContext().sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
fn_imagespath(); // Call method to fetch latest images.
}
}, 1000); // additional delay time of 1 sec to update media scanner
}
}
Just a better method to move file
private void moveFile(File file_Source, File file_Destination, boolean isCopy) throws IOException {
FileChannel source = null;
FileChannel destination = null;
if (!file_Destination.exists()) {
file_Destination.createNewFile();
}
try {
source = new FileInputStream(file_Source).getChannel();
destination = new FileOutputStream(file_Destination).getChannel();
long count = 0;
long size = source.size();
while ((count += destination.transferFrom(source, count, size - count)) < size) ;
if (!isCopy) {
file_Source.delete();
}
} finally {
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
}
Not much changes in code added MediaScannerConnection. Give it a try .
private class LongOperation extends AsyncTask<String, Void, Integer> {
#Override
protected Integer doInBackground(String... params) {
int movedCount=0;
for (int i=0;i<selectedImages.size();i++) {
File sourceImage = new File(al_images.get(int_position).getAl_imagepath().get(i));
File destinationImage = new File(al_images.get(int_position).getStr_folder(), ".jpeg");
try {
boolean isMoved= copyOrMoveFile(sourceImage, destinationImage, true);
if(isMoved) {
movedCount++;
callMediaScanner(ImageGallery.this, destinationImage.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
}
return movedCount;
}
#Override
protected void onPostExecute(Integer val) {
super.onPostExecute(val);
// Here you have to modify return type of doInBackground as per your convineance
if(val.intValue()==selectedImages.size()){
Log.e("Moved","Allfile moved");
}else{
Log.e("Moved","Some file missing");
}
}
public boolean copyOrMoveFile(File localFile, File destinationFile, boolean isCopy) {
FileChannel outputChannel = null;
FileChannel inputChannel = null;
try {
outputChannel = new FileOutputStream(destinationFile).getChannel();
inputChannel = new FileInputStream(localFile).getChannel();
inputChannel.transferTo(0, inputChannel.size(), outputChannel);
inputChannel.close();
if (!isCopy)
localFile.delete();
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (inputChannel != null) inputChannel.close();
if (outputChannel != null) outputChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
}
public void callMediaScanner(Context context, String path) {
MediaScannerConnection.scanFile(context,
new String[] { path }, null,null);
}
I have a Server in Java and a Client in Android. In Android I have an AsyncTask for the receive of the video continuous by the server and a Thread that read the video with the MediaPlayer.
I launch the MediaPlayer after 5s but only the receipt packets are read at the moment when the MediaPlayer is launched.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
vidSurface = (SurfaceView) findViewById(R.id.surfView);
ConcurrentLinkedDeque<OutputStream[]> list = new ConcurrentLinkedDeque<>();
Connexion connexion = new Connexion(list);
connexion.execute();
new Thread(new Task2(list)).start();
}
private class Connexion extends AsyncTask<Void, Void, Void> {
private ConcurrentLinkedDeque<OutputStream[]> list;
public Connexion(ConcurrentLinkedDeque<OutputStream[]> list) {
this.list = list;
}
#Override
protected Void doInBackground(Void... params) {
ConcurrentLinkedDeque<OutputStream[]> list = new ConcurrentLinkedDeque<>();
DownloadVideo dv = new DownloadVideo(list);
dv.connexion();
return null;
}
}
public void launchVideo() {
Thread.sleep(5000);
vidHolder = vidSurface.getHolder();
vidHolder.addCallback(this);
}
class Task2 implements Runnable {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
Log.d("1", "Thread2");
launchVideo();
}
});
}
Thanks a lot.
Download Video :
public void connexion() {
try {
client = new Socket(IP_SERVER, 9003); // Creating the server socket.
if (client != null) {
// Receive video
InputStream in = client.getInputStream();
OutputStream out[] = new OutputStream[1];
// Store on device
out[0] = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Movies/chrono2.mp4");
byte buf[] = new byte[1024];
int n;
while ((n = in.read(buf)) != -1) {
out[0].write(buf, 0, n);
//Adding last in the queue
list.addLast(out);
Log.d("byte" , "" + out);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I created IGoogleServices interface on core project:
public interface IGoogleServices {
.....
public String getPlayerName();
}
Then I put the following code inside the implementaion method in MainActivity on android project:
private String name;
#Override
public String getPlayerName() {
runOnUiThread(new Runnable() {
public void run() {
name = Games.Players.getCurrentPlayer(_gameHelper.getApiClient()).getDisplayName();
}
});
return name;
}
This is simple way to tell MyGame the player name as a String:
lblPlayerName = new Label("" + googleServices.getPlayerName(), style);
stage.addActor(lblPlayerName);
BUT I can't do it in the image case.
In the same previous String case, I tried to do it in the image case:
private Uri imgUri;
#Override
public void requestImgProfile() {
runOnUiThread(new Runnable() {
public void run() {
imgUri = Games.Players.getCurrentPlayer(_gameHelper.getApiClient()).getIconImageUri();
ImageManager manager = ImageManager.create(MainActivity.this);
manager.loadImage(new ImageManager.OnImageLoadedListener() {
public void onImageLoaded(Uri arg0, Drawable drawable, boolean arg2) {
try {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
FileOutputStream out = new FileOutputStream(imagePath);;
out = openFileOutput(imgUri + ".png", Context.MODE_MULTI_PROCESS);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}, imgUri);
};
});
}
My imgUri is:
content://com.google.android.gms.games.background/images/26s5523b/2633
What is the class type (like String) which I can tell MyGame it and libGDX libraries can treat with it?
If you have an Image URI, you can get a Bitmap via:
Uri imgUri;
Bitmap bitmap = null;
try {
InputStream inputStream = getContentResolver().openInputStream(imgUri);
bitmap = BitmapFactory.decodeStream(inputStream);
} catch (FileNotFoundException e) {
}
This works whether it is a file:// URI or content:// URI.
Then use solutions such as this answer to convert the Bitmap into something usable by libGDX.
I am using OpenCV4Android and capturing camera frames, so each time a frame has been captured, the method Mat onCameraFrame(CvCameraViewFrame inputFrame) is called. Inside that method I receive the frames in a Mat object (mRgba).
When a button has been clicked, I start sending out those frames to a connected FTP server. When that button has been clicked again, I stop sending the frames. I need to be putting this on a separate thread, as sending out these frames causes a lot of GUI lag.
What I am struggling to conceptualize and code is... to be able to have one thread that starts and stops on button clicked, and initialize and start the thread once - when started, receives each camera frame from the camera and pushes that camera frame out to the FTP server.
Currently I have this:
public class FdActivity extends Activity implements CvCameraViewListener2 {
private FTPImage ftp;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//connect to FTP server
ftp = new FTPImage();
Runnable runnable = new Runnable() {
#Override
public void run() {
String ftpResult = ftp.connectToFTP(config.getFtpIP());
}
};
new Thread(runnable).start();
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
if (isRecordFrames) {
Bitmap bmp;
try {
//convert Mat to Bitmap
bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mRgba, bmp);
//scale down Bitmap
final Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth()/2, bmp.getHeight()/2, false);
//issue - this is going to create a Runnable for each frame
Runnable runnable = new Runnable() {
#Override
public void run() {
ftp.writeImageToFTP(scaledBmp);
}
};
new Thread(runnable).start();
}
catch(Exception ex) {
Log.i(TAG, "Exception onCameraFrame");
}
}
return mRgba;
}
}
FTPImage Class:
public class FTPImage {
private FTPClient ftpClient = null;
public String connectToFTP(String ftpIP) {
ftpClient = new FTPClient();
String reply = "";
try {
ftpClient.connect(InetAddress.getByName(ftpIP));
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
ftpClient.disconnect();
}
ftpClient.enterLocalPassiveMode();
ftpClient.login("anonymous", "");
reply = ftpClient.getReplyString();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
if (ftpClient.isConnected()) {
try {
ftpClient.disconnect();
}
catch (IOException ex)
{ }
}
e.printStackTrace();
}
return reply;
}
public void writeImageToFTP(Bitmap b) {
if (ftpClient != null) {
if (ftpClient.isConnected()) {
try {
boolean res = ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
b.compress(CompressFormat.PNG, 100, stream);
BufferedInputStream buffIn = new BufferedInputStream(
new ByteArrayInputStream(stream.toByteArray()));
res = ftpClient.storeFile("testImg.jpg", buffIn);
//when res returns, storeFile has finished uploading
String reply = ftpClient.getReplyString();
buffIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void disconnectFromFTP() {
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
How can I pass each camera frame to a single, separate, running thread which then sends out that frame to a FTP server?
I hope that makes sense... just having trouble with the threading part. Thank you in advance!
I have tried to add Thread UploadThread here :
public class FdActivity extends Activity implements CvCameraViewListener2 {
private FTPImage ftp;
// declaration
private UploadThread mUploadThread;
private Handler mUploadHandler;
public static int UPLOAD_IMAGE = 11;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialization
mUploadThread = new UploadThread(this);
mUploadThread.start();
//connect to FTP server
ftp = new FTPImage();
Runnable runnable = new Runnable() {
#Override
public void run() {
String ftpResult = ftp.connectToFTP(config.getFtpIP());
}
};
new Thread(runnable).start();
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
if (isRecordFrames) {
Bitmap bmp;
try {
//convert Mat to Bitmap
bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mRgba, bmp);
//scale down Bitmap
final Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, bmp.getWidth()/2, bmp.getHeight()/2, false);
Message msg = Message.obtain();
msg.what = UPLOAD_IMAGE;
mUploadHandler.sendMessage(msg); // this way we send message to upload thread queue
}
catch(Exception ex) {
Log.i(TAG, "Exception onCameraFrame");
}
}
return mRgba;
}
Thread UploadThread = new Thread() {
private Context;
public UploadThread(Context context) {
this.context = context;
}
#Override
public void run() {
Looper.prepare();
mUploadHandler = new Handler() {
public void handleMessage(msg) {
case UPLOAD_IMAGE:
ftp.writeImageToFTP(scaledBmp); // uploading
break;
}
}
Looper.loop();
}
};