I added a feature based on the Codelabs tutorial from Google (https://codelabs.developers.google.com/codelabs/sceneform-intro/index.html?index=..%2F..index#15) which allows users to take photos of AR objects that were added into the scene. The code works fine, however, I wish to hide the PlaneRenderer (the white dots that appear when ARCore detects a surface) in the photo taken by users.
In the onClickListener for the "Capture Photo" button, I tried setting PlaneRenderer to invisible before the takePhoto() is called. This hid the PlaneRenderer on screen, but not in the photo captured.
This is my onClickListener:
capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
for (TransformableNode vNode : videoNodeList){
if (vNode.isSelected()){
vNode.getTransformationSystem().selectNode(null);
}
}
takePhoto();
}
});
videoNodeList contains a list of transformableNodes, and is used to keep track of the objects added by users (as users can add more than 1 object into the scene). As the objects are transformableNodes, users can tap on them to resize/rotate, which shows a small circle underneath the selected object. So, the for-loop added is to de-select all transformableNodes when taking photos, to ensure that the small circle does not appear in the photo as well.
The takePhoto() method is from the CodeLabs tutorial, and is given as follows:
private void takePhoto() {
final String filename = generateFilename();
ArSceneView view = arFragment.getArSceneView();
// Create a bitmap the size of the scene view.
final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
Bitmap.Config.ARGB_8888);
// Create a handler thread to offload the processing of the image.
final HandlerThread handlerThread = new HandlerThread("PixelCopier");
handlerThread.start();
// Make the request to copy.
PixelCopy.request(view, bitmap, (copyResult) -> {
if (copyResult == PixelCopy.SUCCESS) {
try {
File file = saveBitmapToDisk(bitmap, filename);
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
} catch (IOException e) {
Toast toast = Toast.makeText(ChromaKeyVideoActivity.this, e.toString(),
Toast.LENGTH_LONG);
toast.show();
return;
}
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
"Photo saved", Snackbar.LENGTH_LONG);
snackbar.setAction("Open in Photos", v -> {
File photoFile = new File(filename);
Uri photoURI = FileProvider.getUriForFile(ChromaKeyVideoActivity.this,
ChromaKeyVideoActivity.this.getPackageName() + ".provider",
photoFile);
Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
intent.setDataAndType(photoURI, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
});
snackbar.show();
} else {
Toast toast = Toast.makeText(ChromaKeyVideoActivity.this,
"Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
toast.show();
}
handlerThread.quitSafely();
}, new Handler(handlerThread.getLooper()));
}
To give you a clearer picture, PlaneRenderer is hidden on the device screen when the "Capture Photo" button is tapped. This is what is seen immediately after the user taps on the "Capture Photo" button:
However, PlaneRenderer still appears in the photo taken. This is the resulting image that was taken:
which is not what I was looking for as I want to hide the PlaneRenderer in the photo (ie. photo taken should not have the white dots)
Users of this app add objects by selecting an object from the menu and tapping on the PlaneRenderer, so disabling the PlaneRenderer totally is not feasible. In addition, I have another video recording feature in the app that managed to successfully hide the PlaneRenderer in the recording by simply setting PlaneRenderer to invisible, so I am not sure why it doesn't work when capturing photos.
Any help will be greatly appreciated! :)
Finally figured this out after countless of hours. Sharing my solution (may not be the best solution) in case anyone faces this same issue in the future.
I discovered that due to the handlerThread used, the takePhoto() method always happens before the PlaneRenderer was set to invisible whenever the button is tapped. So, I added a short delay to ensure that the reverse happens - ie. delay takePhoto() method for a short while such that the method will always happen after the planeRenderer is invisible.
Here is the code snippet:
capturePhotoBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
arFragment.getArSceneView().getPlaneRenderer().setVisible(false);
for (TransformableNode vNode : videoNodeList){
if (vNode.isSelected()){
vNode.getTransformationSystem().selectNode(null);
}
}
v.postDelayed(new Runnable() {
public void run() {
takePhoto();
}
}, 80);
}
});
This method worked for me, but I am sure there are better solutions to solve this problem. Hope this helps someone with the same problem and feel free to contribute if you know of a better solution.
Related
I have an activity that loads multiple fragments depending on user action (button click) or event (FCM Data Message which triggers a LocalBroadcast).
I hit a snag recently when I put a Fragment Transaction inside a BroadcastReceiver, and as soon as the receiver gets triggered, instead of loading up the next fragment, I get the first (default) fragment which is loaded in the OnCreate of the activity, implying that the Activity has reset/restarted somehow.
Given the speed of this, the only error I managed to see before the logcat on Android Studio reset was this :
java.lang.IllegalStateException: Can not perform this action after
onSaveInstanceState
Digging around hasn't helped much, except for this article on Activity State Loss which I discovered. It is rather old (2013) but seems to make sense. However, there is no solution that I can think of, short of making my fragment a bit more complicated, and handing the next fragment's logic in this one itself.
Please find the bit of code where this happens below.
BroadcastReceiver assistanceReceivedStatusReceiver = new BroadcastReceiver() {
public void dummyfunc(){
return;
}
#Override
public void onReceive(Context context, Intent intent) {
// this is triggered by the localBroadcast from FCM Service
boolean requestresult = intent.getBooleanExtra("success", true);
if(!requestresult) {
// we don't have a responder
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: UNABLE TO FIND A RESPONDER");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
String message = "Unable to find you a responder, please try again!";
frameAnimation.stop();
txtRequestStatus.setText(message);
dialogButtonLayout.setVisibility(View.VISIBLE);
showBottomAppBar();
showMenuFab();
moveMenuRight();
setMenuImage(R.drawable.baseline_undo_white_24dp);
menuButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dialogLayout.setVisibility(View.GONE);
waitingLayout.setVisibility(View.VISIBLE);
moveMenuCenter();
resetMenuImage();
menuButton.setOnClickListener(defaultMenuButtonListener);
}
});
} else {
// we have a responder
// this is a one time receiver - set up an Observable for the Live<Incident>
// and unregister self.
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: RECEIVED A RESPONDER");
Log.d(TAG, "onReceive: =======================================");
Log.d(TAG, "onReceive: =======================================");
inIncident = true;
Bundle nextbundle = new Bundle();
responderinfo = intent.getBundleExtra("responderinfo");
nextbundle.putParcelable("data", intent.getBundleExtra("data"));
nextbundle.putBundle("responderinfo", responderinfo);
// GO! GO! GO!!!
//startFragmentWithArgs(new RequestAssistFragmentDeliver(),nextbundle );
RequestAssistFragmentDeliver deliver = new RequestAssistFragmentDeliver();
deliver.setArguments(nextbundle);
((Reviv) getActivity()).getSupportFragmentManager()
.beginTransaction()
.replace(R.id.containerFrameLayout, deliver).commitAllowingStateLoss();
}
}
};
Any ideas on what's going wrong? As always, I'm happy to share more info based on what is needed (the code base is humongous, and knowing what is needed helps me share the relevant segments).
UPDATE 1 :
Sharing the functions as requested by Udit. These are wrapper functions, to help make the code a little more readable. The bottomAppBar (BottomAppBar) and menuButton (FAB) are views that are loaded in the Activity, and I make associations in each Fragment by calling a getter defined in the Activity.
(MainActivity)getActivity.getBottomAppBar();
Functions:
private void showBottomAppBar(){
bottomAppBar.setVisibility(View.VISIBLE);
menuButton.setVisibility(View.VISIBLE);
}
private void moveMenuRight(){
bottomAppBar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_END);
}
private void showMenuFab(){
//bottomAppBar.setFabAttached(true);
menuButton.setVisibility(View.VISIBLE);
}
From what I can figure out, your activity is restarting because of the crash while adding the fragment (java.lang.IllegalStateException).
To confirm, you can replace
.commit()
method with
.commitAllowingStateLoss()
and see if this solves your problem
I have a horizontal scroll view, with several imageviews inside of it.
If I click once on the images, I want to show a toast. This toast has to be changed depending on a stringvariable that is set in a textview.
If there is a doubleclick on an image, I need to open a new activity that opens a PDF (embedded into the app).
At the moment I have:
public class activity_fruit extends AppCompatActivity {
TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Fruit");
setContentView(R.layout.activity_fruit);
textView = (TextView) findViewById(R.id.txtstatus);
textView.setText(((Globals) this.getApplication()).getTaal());
int fruit1 = R.drawable.kumato;
ImageView targetImageView1 = findViewById(R.id.img_fruit1);
targetImageView1.setOnClickListener(new DoubleClickListener() {
#Override
public void onSingleClick(View v) {
if(textView.equals("ENG")) {
Toast.makeText(getApplicationContext(), "eng single", Toast.LENGTH_SHORT).show();
}
if (textView.equals("")) {
Toast.makeText(getApplicationContext(), "nl single", Toast.LENGTH_SHORT).show();
}}
#Override
public void onDoubleClick(View v) {
Toast.makeText(getApplicationContext(), "double", Toast.LENGTH_SHORT).show();
}
});
Glide.with(this)
.load(fruit1)
.into(targetImageView1);
Everything loads just fine, but since adding the if statement to my onSingleClick(), the one tap doesn't work anymore. The double tap works just fine.
As said in the first few lines of this post, I also want to change my double tap code to open a new activity and then open a PDF (that is stored in the app itself) inside the app itself. So any help with that would also be appreciated.
Here:
textView.equals("ENG"))
You are comparing the text view object with a String. That will always return false!
Instead - retrieve the text from the textView - and compare that string with your other strings! As in:
textView.getText().equals("ENG")
You are like comparing a "box for eggs" with an egg. Instead, you first have to fetch an egg from that box, then you can compare that egg to your other egg.
instead of textView.equals("ENG")
Use this.
if(textView.getText().equals("ENG")) {
Toast.makeText(getApplicationContext(), "eng single", Toast.LENGTH_SHORT).show();
}
Try this
Use This
textView.getText().toString().trim().equals("ENG")
{
Toast.makeText(getApplicationContext(), "eng single", Toast.LENGTH_SHORT).show();
}
instead of
textView.equals("ENG")
I have to design an app in which on click of the neutral button, an image of the alert dialogue gets stored in the sdcard.
So I decided to programatically take a screenshot and then use the BitmapDecodeRegion class to crop the image.
But when i take the screenshot, the alert dialogue does not appear in it since it's not attached to the window.
How can I attach it to the window?
here is the code snippet:
public void btnClick(View v) {
Log.d("", "logger button clicked");
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("This is a demo!");
dialog.setMessage("Lets see if this works");
dialog.setCancelable(false);
dialog.setNeutralButton("Take Snap",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Bitmap bitmap;
View v1 = findViewById(android.R.id.content)
.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
GlobalObj.screenImg = bitmap;
v1.setDrawingCacheEnabled(false);
Intent i = new Intent(MainActivity.this,
ViewActivity.class);
startActivity(i);
}
});
AlertDialog newDialog = dialog.create();
newDialog.show();
Kindly help me out.
Try out this library: https://github.com/jraska/Falcon.
It takes screenshots of all active windows of application including dialogs and could figure out your problem.
In your code change this line:
View v1 = findViewById(android.R.id.content)
.getRootView();
to this:
View v1 = AlertDialog.class.cast(dialog).getWindow().getDecorView().getRootView();
It works for me and does not need rooted phone. However this captures only the alert dialog on a black background, without the underlying views from your activity. If you want to have everything (not that simple) you can try this answer from Aswin Rajendiran.
I have a Android app that includes a wallpaper picker.
It loads thumbnails into a gallery widget and when one is selected it displays in a large imageview behind the gallery. All in one Activity
All the wallpapers and thumbs are loading from web by a manifest parser and node. They load just fine.
Problem is that when the menu item to save/apply the wallpaper is pressed before the imageview is loaded has issues. If i wait until the imageview loads it no issue.
I tried getting around this by doing the following.
I had the mImageDrawableSet boolean set to false unless the loading was complete
ImageLoader.getInstance().displayImage(mNode.url, mImageView, new ImageLoadingListener() {
#Override
public void onLoadingStarted () {
mImageDrawableSet = false;
mImageView.setVisibility(View.GONE);
mPending.setVisibility(View.VISIBLE);
}
#Override
public void onLoadingFailed (FailReason failReason) {
mImageDrawableSet = false;
Toast.makeText(getActivity(), "Image Failed To Load!", Toast.LENGTH_SHORT).show();
}
#Override
public void onLoadingComplete (Bitmap bitmap) {
mImageDrawableSet = true;
mImageView.setVisibility(View.VISIBLE);
mImageView.setImageBitmap(bitmap);
mImageView.setZoomable(true);
mPending.setVisibility(View.GONE);
if (mApplyImageOnDisplay)
applyImage();
if (mSaveImageOnDisplay)
exportImage();
}
#Override
public void onLoadingCancelled () {
}
});
Then when the exportImage() is called it looks to see if mImageDrawableSet it flase and if it is then it sets mSaveImageOnDisplay to true. So, when the loading is complete above it will call the exportImage() and process beyond what i posted below.
public void exportImage () {
if (this.mImageDrawableSet == false) {
this.mSaveImageOnDisplay = true;
return;
}
try {
final Bitmap bitmap = getImageBitmap();
That example from above was from a code where it showed a single imageview in one fragment. I am now using a Activity where all wallpapers are shown in one imageview depending on which thumbnail is selected.
Also I ditched UIL and am not using Picasso and it doesn't a loading started. just loading failed and complete.
So the only way that i have been able to make this work is create a boolean that is set to false on activity start.
Then on option menu item i have this
if (ReadyBoolean.getValue()){
setProgressBarIndeterminateVisibility(true);
applyCrop();
} else {
Toast.makeText(this, "Please wait until wallpaper loads", Toast.LENGTH_SHORT).show();
}
return true;
On the picasso loading complete i set it to true, then back to false after the save function runs.
So, it works but i hate i have to display a message that say please wait until wallpaper loads. I wish it would just work after the imageview loads. I can't seem to figure out how to do it
I'm coding a language dictionary running on Android 2.2. My scenario is that I want to add words to the Favourite list and then view the saved favourites. In fact, I want to save favourite words to a (text) file created on the sdcard, and when user click on "View Favourites", words from this file will be shown and can be chosen.
The problem is I have no idea how to code "saving and viewing" the Favourite list.
Here is my code:
btnAddFavourite = (ImageButton) findViewById(R.id.btnAddFavourite);
btnAddFavourite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// I want the code here!
Toast toast = Toast.makeText(ContentView.this, R.string.messageWordAddedToFarvourite, Toast.LENGTH_SHORT);
toast.show();
}
});
btnAddFavourite.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// Open the favourite Activity, which in turn will fetch the saved favourites, to show them.
Intent intent = new Intent(getApplicationContext(), FavViewFavourite.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(intent);
return false;
}
});
I wonder whether there is a solution to this problem and how I can code to address this problem. Thank you very much.
Hmm, a solution to what problem ? Even if a database would have been a better solution for that, text file should work too. what's wrong ?