I have a recyclerview in an activity in android with various types of Item objects ( bound to Different views). when I click on a button on one of the recyclerview views, It is supposed to fire an event which in turn opens a new activity. This works the first time i tap it, but, when I try to enter the activity again This click event causes the activity to freeze? the only debug output im getting is this. No outofmemoryexception or anything helpful like that.
[Mono] [0xb96777f0] worker finishing
Thread finished: <Thread Pool> #5
[ViewRootImpl] changeCanvasOpacity: opaque=false
[ViewRootImpl] changeCanvasOpacity: opaque=false
[Mono] [0xba234a20] worker finishing
when I click on the "Button". Which is a is a relativelayout with a click listner attached. I add data to a bundle an call StartActivity(). below is my method to open this activity
public override void openActivity(object sender, EventArgs e)
{
RelativeLayout btn = (RelativeLayout) sender;
int pos = (int) btn.GetTag (Resource.Id.bottom_btn);
TweetPicItem picitem = (TweetPicItem) newsitems[pos];
var tweetactivity = new Intent(cont, typeof(TweetActivityImage));
tweetactivity.PutExtra("content", picitem.BodyText);
tweetactivity.PutExtra("name", picitem.TeamName);
tweetactivity.PutExtra("image_url", picitem.TweetImage);
tweetactivity.PutExtra("club", picitem.CreatedBy);
tweetactivity.PutExtra ("tint", picitem.BackgroundColour);
tweetactivity.PutExtra("isliked", picitem.ILikeThis);
tweetactivity.PutExtra("tweetid", picitem.Id);
if(picitem.Comments != null)
tweetactivity.PutExtra("comments" , JsonConvert.SerializeObject(picitem.Comments));
cont.StartActivity(tweetactivity, options.ToBundle());
}
and in onBind I detach before reattaching the eventhandler each time
vh.bottombtn.SetTag(Resource.Id.bottom_btn, position);
vh.bottombtn.Click -= openActivity;
vh.bottombtn.Click += openActivity;
So essentially the app freezes when i press a button. I cant really see where this issue is coming for due to lack of debug output? First i thought it was attaching eventhandlers in onbind but iam explicitly detaching evenhandler before attaching it each time? Ive also tried it on multiple devices and the issue persists. I know there is very little info to work with here, I am stumped myself, I thought the debug out put would warn me somehow but iam getting nothing back. If anyone has any ideas id appreciate the help/ suggestions.
Related
I have a problem I have not yet been able to understand, nor solve.
On my android project, I have a BaseActivity. There, among other functions, I decided to add a function to show or hide a loading view when necessary. It works as intended, but sometimes an error happens.
I will try to raise some important info about my project I think can be useful. My app is integrated with an external login app. I call it when the services I call need to refresh it's token. When the user logs in on the app, it calls a listener and give me back control on mine.
The problem is the next one:
I come to an activity that needs to call a service and I have the token all right, but then I lock the phone. After a long period of time, I unlock the phone and, from the same activity, I call again the service. My activity shows de loader as intended and, as my token is expired, I call the login app from it's SDK.
When I come back to my app, and I call the service I wanted successfully, the app tries to hide the loader. This is where the fail comes, as I can't change the visibility to GONE. I looked for it on the view hierarchy and find it, but with visibility = VISIBLE.
Here is the piece of code from the loader, hope someone can find where I'm making the mistake!
abstract class BaseActivity : DaggerAppCompatActivity(){
// These are the IDS of the Views I'm adding to the activity, so I can track them and change their visibility
var imgLoadingID = -1
var rvLoadingID = -1
fun showLoading() {
// If the views are added I show them
if (imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.VISIBLE
imageView.visibility = VISIBLE
imageView.isClickable = false
imageView.isFocusable = false
} else {
// else I create them and show them
val imgLoading = ImageView(this)
imgLoading.id = View.generateViewId()
imgLoadingID = imgLoading.id
val maxpx = CustomUtils.ViewUtils.converIntToDps(65, this)
Glide.with(this).asGif().load(R.mipmap.loading).into(imgLoading)
val relativeLayout = RelativeLayout(this)
relativeLayout.id = View.generateViewId()
rvLoadingID = relativeLayout.id
var params = RelativeLayout.LayoutParams(maxpx, WRAP_CONTENT)
params.addRule(RelativeLayout.CENTER_IN_PARENT)
relativeLayout.addView(imgLoading, params)
relativeLayout.background = getDrawable(R.color.pure_white_97)
relativeLayout.isClickable = true
relativeLayout.isFocusable = true
findViewById<ViewGroup>(android.R.id.content).addView(relativeLayout, RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT))
imgLoading.visibility = VISIBLE
}
// I lock the back button so people don't cancel my requests
esBackPressedBlocked = true
}
// Here I find the views and change their visibility.
fun hideLoading(){
if(imgLoadingID > 0 && rvLoadingID > 0) {
val imageView = findViewById<ImageView>(imgLoadingID)
val relativeLayout = findViewById<RelativeLayout>(rvLoadingID)
relativeLayout.visibility = View.GONE
imageView.visibility = View.GONE
}
esBackPressedBlocked = false
}
}
I deleted some logs I added to the whole function, but when it fails, it enters on the hideLoading() function, even on the relativeLayout.visibility = View.GONE part.
The function used to be with the Views as an whole object instead of their ids, but I found it more reliable this way, and saving the views instead of their Id's had the same problem.
My main concern is how Android manages my application while the phone is locked for this period of time (the fails happened after 8-10 hours of inactivity). I think something there can be creating this issue. I thought also about the external Login app, since it's sdk is launching their app's intent and calling me from a listener when coming back, but, since my code is being executed, I think Android it's managing my views on an strange way. Or maybe I try to hide the loading view before I'm on the resumed activity... I don't really know.
BTW, I know there are easier solutions on showing a loader, but I wanted to create it the cleanest way. If you have any cleaner approach I'm open to any solution.
If there is anything unclear let me know in the comments, and I hope my English was clear enough to express myself, it's a tricky problem that I can't understand, so it's difficult for me to explain it.
Thanks!!
I'm using AppGyver Steroids and Supersonic to build an app and I'm having some issues navigating between views programmatically.
Based on the docs, you navigate between views like this:
var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.push(view_obj);
However, when I inspect things via the Chrome DevTools, it appears that a second duplicate view is created i.e. If I navigate away from the index page and then navigate back, I now have two index pages, instead of what [I think] should be one. It also doesn't close the previous view I was on.
How can I prevent this from happening and simply move to the existing view, instead of duplicating views? How do I close a view after I have navigated away from it?
Thanks.
The problem you're encountering is that you're creating a new supersonic.ui.View("main#index") every time you navigate. On top of this, I think you want to return to the same view when you navigate back to a view for the second time, i.e. you want the view to remain in memory even if it has been removed from the navigation stack with pop() (rather than pushing a new instance of that view). For this, you need to preload or "start()" the view, as described in the docs here.
I implemented my own helper function to make this easier; here is my code:
start = function(dest, isModal) {
var viewId=dest,
view=new supersonic.ui.View({
location: dest,
id: viewId
});
view.isStarted().then(function(started) {
if (started) {
if (isModal) {supersonic.ui.modal.show(view);}
else {supersonic.ui.layers.push(view);}
} else {
// Start Spinner
supersonic.ui.views.start(view).then(function() {
if (isModal) {supersonic.ui.modal.show(view);}
else {supersonic.ui.layers.push(view);}
// Stop Spinner
}, function(error) {
// Stop Spinner
A.error(error);
});
}
});
};
Use it like start('module#view');. As a bonus, you can pass true as the second argument and it gets pushed as a modal instead.
It checks if you've already started a view - if so, it just pushes that view back onto the stack. If not, it start()s (i.e. preloads) it, then pushes it. This ensures that the view stays in memory (with any user input that has been modified) even when you pop() it from the stack.
You have to imagine that the layer stack is actually a stack in the Computer Science sense. You can only add and remove views at the top of the stack. The consequence of this is that complex navigations such as A > B > C > D > B are difficult/hacky to do (in this case, you'd have to pop() D and C in succession to get back to B).
Views will close if you pop() them, as long as you didn't start() them. If you did, and you pop() them, they remain in memory. To kill that view, you have to call stop() on it, as described in the docs I linked above.
try
var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.replace(view_obj);
And take a look at supersonic.ui.layers.pop();
Thanks to LeedsEbooks for helping me get my head around this challenge. I was able to find a solution. Here is the code:
var start = function(route_str, isModal) {
var regex = /(.*?)#(.*)/g;
var match_obj = regex.exec(route_str);
var view_id_str = match_obj[2],
view_location_str = route_str,
view = new supersonic.ui.View({
location: view_location_str,
id: view_id_str
});
view.isStarted().then(function(started) {
if (started)
{
if (isModal)
{
supersonic.ui.modal.show(view);
}
else {
supersonic.ui.layers.push(view);
}
}
else
{
// Start Spinner
supersonic.ui.views.start(view).then(function() {
if (isModal)
{
supersonic.ui.modal.show(view);
}
else
{
supersonic.ui.layers.push(view);
}
// Stop Spinner
}, function(error) {
// Stop Spinner
A.error(error);
});
}
});
};
You must ensure that your route has the format module#view as defined in the documentation.
PLEASE NOTE
There seems to some problem with the supersonic ui method for starting views. If you run the following code:
supersonic.ui.views.start("myapp#first-view");
supersonic.ui.views.find("first-view").then( function(startedView) {
console.log(startedView);
});
You'll notice that your view id and location are identical. This seems to be wrong as the id should be first-view and location should be myapp#first-view.
So I decided to not use the AppGyver methods and create my own preload method instead, which I run from the controller attached to my home view (this ensures that all the views I want to preload are handled when the app loads). Here is the function to do this:
var preload = function(route_str)
{
var regex = /(.*?)#(.*)/g;
var match_obj = regex.exec(route_str);
var view = new supersonic.ui.View({
location: route_str,
id: match_obj[2]
});
view.start();
};
By doing this, I'm sure that the view will get loaded with the right location and id, and that when I use my start() function later, I won't have any problems.
You'll want to make sure that your structure.coffee file doesn't have any preload instructions so as not to create duplicate views that you'll have problems with later.
Finally, I have a view that is 2 levels in that is a form that posts data via AJAX operation. I wanted the view to go back to the previous view when the AJAX operation was complete. Using my earlier function resulted in the push() being rejected. It would be nice if AppGyver Supersonic could intelligently detect that pushing to a previous view should default to a layers.pop operation, but you don't always get what you want. Anyway, I managed to solve this using supersonic.ui.layers.pop(), which simply does what the Back button would have done.
Everything working as intended now.
I'm trying to make it possible to navigate through my Google Glass application by using head gestures. I'm able to recognize head gestures like looking to the right left and up. They each have their own method for what to do when this gesture is recognized
Now I need to simulate the corresponding touch gestures inside each method. So it will think I'm swiping to the left or right which will allow me to navigate through the cards with the head gestures.
Does anyone have any idea on how to actually achieve this?
Edit
I created a quick hello world application to play with. I added my headgesture code and started trying to get the keys working.
I added the following to my onCreate()
Instrumentation instr = new Instrumentation();
Then I added the following lines to each respective headgesture method.
Headgesture upwards should correspond with tapping the touchpadinst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER)
Headgesture to the left should correspond with swiping left on the touchpad inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_LEFT);
Headgesture to the right should correspond with swiping right on the touchpadinst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_RIGHT);
They are responding accordingly now, however I'm getting an exception saying:
java.lang.RuntimeException: This method can not be called from the main application thread
The Solution
In the end I went a different direction then the one I mentioned in my edit above.
I found out that it is possible to call touch controls in the shell by using
adb shell input keyevent <keycode here>
I then found a way to use this in android, I have the following class named issueKey
public class issueKey {
public void issueKey(int keyCode)
{
try {
java.lang.Process p = java.lang.Runtime.getRuntime().exec("input keyevent " + Integer.toString(keyCode) + "\n");
} catch (Exception e) {
Log.wtf("IssueKeyError", e.getMessage());
}
}
}
Then I simply call the class and pass the keycode for the corresponding gesture
mIssueKey.issueKey(4);//functions as swipe down
Here is the list of keycodes that I tested for anyone that is interested.
Keys for each respective button/gesture
4: Swipe Down
21: Swipe Left
22: Swipe Right
23: Tap
24: Volume Up
25: Volume Down
26: Lock/Unlock Screen
27: Camera Button
However, what I'm wondering now is. What would be best practice, getting the solution I metioned in my edit to work by using a asyncTask or is the solution I'm currently using better.
Using the Instrumentation class would work if you use a separate thread to call the sendKeyDownUpSync method from.
This can be done using the following steps:
Create and start a thread from your activity
In the run method, use the Looper class and create a Handler as explained here
Every time you want to call sendKeyDownUpSync, post a Runnable instance to the Handler, which calls sendKeyDownUpSync in its run method.
A similar code sample (not from me) is available here
I'm building an app that requires me to append a bunch of views into a scrollView (as many as 70) based on the data the server sends back. An abbreviated version of how I am appending them looks like this.
if (mWallPosts != null) {
for (Map.Entry entry : mWallPosts.entrySet()) {
addPostToView((DataClass) entry.getValue(), (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE), mViewGroup);
}
}
and this being the addPostToView method call
View postView = inflater.inflate(R.layout.fragment_wall_post, container, false);
LinearLayout containerView = (LinearLayout) mWallView.findViewById(R.id.wall_content);
containerView.addView(postView);
There is more work that goes on, but it is irrelevant for the purpose of this post. I am also using a NavgationDrawer and the problem I am having is that when I navigate to this item the animation of closing the drawer isn't smooth (it kind of hangs for a second and snaps shut) It's pretty obvious to me that this is caused by the fact that this appending and the closing of the drawer are happening on the same thread, but I'm not sure how to resolve this issue. If I could figure out how to delay the append or something like that it would probably help. I don't think async would be of any use since it is ultimately going to run on the UI thread.
I have three views in my app and I would like to be able to hide them and bring them back selectively. I have tried using the following triggered by a button click event:
if(SurfaceView().isShown()==true){
SurfaceView().setVisibility(View.INVISIBLE);
}else if(!SurfaceView().isShown()){
SurfaceView().setVisibility(View.VISIBLE);
}
However this usually freezes up the views and then closes the app with no errors except the following :
NvRmChannelSubmitt: NvError_IoctlFailed with error code 14
EDIT: the view that Im referring to is actually the _glsurfaceview in the renderactivity from min3d
public class RendererActivity extends Activity implements ISceneController
{
public Scene scene;
protected static GLSurfaceView _glSurfaceView;
My main activity extend RenderActivity
EDIT: added logcat dump
The only things showing up upon the closing are these
03-02 15:57:00.338: I/Min3D(28364): Renderer.onSurfaceChanged()
03-02 15:57:00.355: D/(28364): NvRmChannelSubmit: NvError_IoctlFailed with error code 14
Looks like you might have a copy/paste typo in your code. Are you trying to show the glSurfaceView if the SurfaceView is invisible? If so, try this:
if( SurfaceView().isShown() )
{
glSurfaceView().setVisibility(View.GONE);
}
else if ( !SurfaceView().isShown() )
{
glSurfaceView().setVisibility(View.VISIBLE);
}
So I fixed my problem. I think when your using opengl and glsurfaceviews you have to change what I have above to the following:
if(glSurfaceView().isShown()==true){
SurfaceView().onPause();<----------------------added to pause view
SurfaceView().setVisibility(View.GONE);
}else if(!glSurfaceView().isShown()){
SurfaceView().onResume();<---------------------added to resume view
SurfaceView().setVisibility(View.VISIBLE);
}
The SurfaceView().onPause() tells the glsurfaceview that I want the renderer to pause and vice versa for onResume().
Now Im not sure this is the end all fix for this situation but it is working for like this.
Using View.GONE will make the view invisible, and will not take up any space in your layout.