Screen blinking when using a webview with flash - android

Edit: I made a demo apk, so you can understand what I mean: http://cl.ly/3g0s1p030j243y0p3m2F
For my application, I want a kind of "Super Power Point", or a keynote (the commercial team will present the product to their customers) using all the Android goodness, gestures, etc... on an Android tablet. As Honeycomb is not yet ready and because we need it before march, we choose some random Froyo Tablet (Archos 101), but my issue is for every tablet/phone I tried.
I made a really great application, but for some animations during the presentation, the customer wanted to use flash animations. Because I couldn't code animations (sort of little movies/ animated graphics) that easily in Android and the lack of time, that seemed to be a good idea.
So, after some search on the Web, I used webview and this code:
WebView mWebView1 = (WebView) findViewById(R.id.webview1);
mWebView1.getSettings().setJavaScriptEnabled(true);
mWebView1.getSettings().setPluginsEnabled(true);
mWebView1.loadUrl("file:///android_asset/graph_01.swf");
This work pretty well, but on every device I tried (Archos 101, Nexus One, Nexus S, Galaxy S, Xperia, Desire, HTC Hero, and really more) every activity with a webview blink, a few milliseconds of black screen, then the animation finally appear.
PS: My layout is quite simple too:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<WebView android:id="#+id/webview1" android:layout_height="fill_parent"
android:layout_width="fill_parent"></WebView>
</LinearLayout>
Please help me, I cannot imagine I am the only one to face this issue.
Thank a lot for any help. You have all my code and the demo apk.

THIS ANSWER ADDED LATER:
So the problem is that Adobe's Flash Player creates a SurfaceView inside the WebView and there is a delay between the SurfaceView appearing and the Flash engine bothering to draw anything. In that small gap the SurfaceView appears totally black.
The solution I've found is to subclass WebView and add a customized SurfaceView to the view tree. Secondly it is also important to suppress the first draw() call to the Adobe view. The code for my subclassed WebView is as follows:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.webkit.WebView;
import android.widget.AbsoluteLayout;
public class FixAdobeWebView extends WebView {
View whiteView;
private boolean eatenFirstFlashDraw;
public FixAdobeWebView(Context context, AttributeSet attrs) {
super(context, attrs);
whiteView = new WhiteSurfaceView(context);
whiteView.setLayoutParams(new AbsoluteLayout.LayoutParams(800, 480, 0, 0));
addView(whiteView);
}
private class WhiteSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
public WhiteSurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
canvas.drawColor(Color.WHITE);
holder.unlockCanvasAndPost(canvas);
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) { }
#Override
public void surfaceDestroyed(SurfaceHolder holder) { }
}
//
// Override drawChild to eat the first draw of the FlashPaintSurface
//
#Override
protected boolean drawChild (Canvas canvas, View child, long drawingTime) {
if (!eatenFirstFlashDraw && child.getClass().getName().equals("com.adobe.flashplayer.FlashPaintSurface")) {
eatenFirstFlashDraw = true;
return true;
}
return super.drawChild(canvas, child, drawingTime);
}
}
The layout XML should declare an instance of this class, i.e. :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/white"
>
<org.test.FixAdobeWebView android:id="#+id/webview1"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
/>
</LinearLayout>
On my Nexus One running CM6.1 this works perfectly in that the WebView appears as solid white until the Flash SurfaceView starts drawing. You will probably need to adapt it a bit, e.g. get rid of the hardcoded 800x480 dimensions, and also destroy the white SurfaceView once it's no longer needed.
ORIGINAL (WRONG) ANSWER:
Looks like Adobe are to blame for this one.
The least awful workaround I've found so far is to :
Make the background of the WebView's parent a compatible colour (i.e. white).
Hide the WebView initially.
Wait until you get the page loaded event, and then queue a runnable to show the WebView after a one second delay.
It means you get a guaranteed one second delay before you can see anything, but it's less jarring than that horrible black flash afaik.
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/white"
>
<WebView android:id="#+id/webview1"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:visibility="invisible"
/>
</LinearLayout>
Code:
final Handler handler = new Handler();
...
final WebView mWebView1 = (WebView) findViewById(R.id.webview1);
mWebView1.getSettings().setJavaScriptEnabled(true);
mWebView1.getSettings().setPluginsEnabled(true);
mWebView1.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
mWebView1.setVisibility(View.VISIBLE);
}
}, 1000);
}
});
mWebView1.loadUrl("file:///android_asset/graph_01.swf");
There might be a better way though that would remove the need for a fixed delay...
It's been a while since I did anything with Flash but I seem to recall there is a way for the Flash control to use the browser's Javascript engine (a quick Google turns up the Flex Ajax Bridge which seems likely).
What you could do is, with the aforementioned bridge, use ActionScript to call a Javascript function which does an alert(). In your Java code you'd hook your mWebView1 up to a WebChromeClient which implements onJsAlert(). In that function you'd make the WebView visible.

Related

Android - Andengine - How to use native UI SKD

I want use the native sdk interface layout, (How a normal app) to design my game menu, and link it to the BaseGameActivity, or GameScene, I know how to design the interface using sdk native, but I dont know how implement it on andengine :S
I cant find any solution, I will hope anybody can help me to find the best method, or the way to use these.
Sorry for my bad english.
More info: I know how to add a little framelayout on my baseactivity, but I can a set of menus (2/3) and that you can move on it, and enter on the game and exit of the game :)
Sorry my english again
Well, I do this works :)
Only create a normal activity, with the layout etc.. and use the intent.putExtra(); to send a particular info to the BaseGameActivy, Then, on onCreateResources() I set a serie of conditions to determine that I press before, and set the wished scene.
Sorry my english :)
EDIT: imported tutorials from original website
How to use UI Android SDK with AndEndine
NOTE : You may have filling errors if you change width and heights in these layouts so be carefull (this solution works with fullscreen usage)
XML layout
In your project's directory res/layout create an empty file named themainactivity.xml and put the following content inside.
Notes : Set the attribute tools:context to your application activity name, beginning with a dot (here: .MyMainActivity)
XML layout file: res/layout/themainactivity.xml
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- code placed here will be above the AndEngine render -->
</RelativeLayout>
Java class
You just have to specify the IDs in your class.
MyMainActivity.java
package com.example;
import org.andengine.ui.activity.SimpleLayoutGameActivity;
public class MyMainActivity extends SimpleLayoutGameActivity
{
#Override
protected int getLayoutID()
{
return R.layout.themainactivity;
}
#Override
protected int getRenderSurfaceViewID()
{
return R.id.gameSurfaceView;
}
}
Create a custom Android SDK layout in AndEngine
XML layout
WARNING: the root node must be a merge node ! Inside this you can do what you want.
XML layout file: res/layout/my_view.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="fill_parent"
android:layout_height="40dp"
android:text="Dat button" />
</LinearLayout>
</merge>
The controller class
For being able to use your interface you have to link it to the XML view using the inflater service.
NOTE: The UI Java code is compiled when you switch to the WYSIWIG editor so if you don't add the linking code below you won't see the contents of the layout in the activities that use it.
Custom layout: MyView.java
package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
public class MyView extends LinearLayout
{
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
// Link to the XML view
LayoutInflater.from(context).inflate(R.layout.my_view, this, true);
// Link to the XML view (alternative using service, you can delete if you don't need it)
//LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//inflater.inflate(R.layout.my_view, this);
}
}
Reuse in an other activity
Just add this code in the activity layout.
<com.example.MyView
android:id="#+id/myView1"
android:layout_width="100dp"
android:layout_height="wrap_content" />

Adding Custom View to XML Layout with OnTouchListener

I've been working on an app where a ball (bitmap) appears on the canvas at the point where the user taps on the screen. The background is an xml layout setContentView(R.layout.newsession). The canvas is a black painted canvas. When i set my java parent class setContentView(customView), the program works fine but when I add the custom surface view to my XML layout and setContentView(R.layout.newsession), the screen just shows the canvas, and the OnTouch event doesn't work. Am I doing something wrong? I've been working on this for almost a week now and I really need help. I will post my code below for the XML layout and custom surfaceView. Thanks in advance!
XML layout (newsession)
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/newSessionPage"
>
<ImageView
android:layout_width="231dp"
android:id="#+id/ivStrikeGrid"
android:layout_gravity="center"
android:layout_height="270dp"
android:layout_marginTop="18dp"
android:src="#drawable/strike_grid"
android:layout_marginBottom="10dp"
/>
<appsys.studios.CustomSurfaceViewOne
android:id="#+id/customSurfaceViewOne1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></appsys.studios.CustomSurfaceViewOne
>
</FrameLayout>
Custom SurfaceView
package appsys.studios;
public class CustomSurfaceViewOne extends SurfaceView implements Runnable{
public CustomSurfaceViewOne(Context context, AttributeSet attr) {
super(context, attr);
ourHolder = getHolder();
}
// Other stuff
}
It works fine like this:
newSeshView = new CustomSurfaceViewOne(this, null);
setContentView(newSeshView);
But nothing happens when I try to use it from the XML layout, like this:
newSeshView = new CustomSurfaceViewOne(this, null);
setContentView(R.layout.newsession);
Thanks again! :)
I think you might be missing invallidate() call of the view on its touch reading delegates.
I am not sure exactly what is happening in your code. But if i would be creating my own view and adding it in my own layout as you did.
And want it to change itself on reading touch events then i would be doing something like
in the myown view class it self
#override
// i dont remem exact signature of this method. google it or see docs
// motive is to read touch event of view and doing appropriate changes
// and redrawing the view again
public void onTouchEvent(MotionEvent me)
{
doCalculation(); // change points where to draw the ball next time. Read me
invalidate(); // tell the view re draw it self
}
Hope it helps :)
I ran into the same issue, and found your question while looking for the answer. I solved it, but I'm not sure it is the proper way.
3 files, your main activity, your surfaceview, and your XML file.
Your XML file looks ok, you have the surfaceview in it
<appsys.studios.CustomSurfaceViewOne
android:id="#+id/customSurfaceViewOne1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></appsys.studios.CustomSurfaceViewOne
>
In your activity, add implements OnTouchListener, use setContentView(R.layout.newsession); and after that, still in your onCreate() add this line CustomViewOne cvo = (CustomViewOne)findViewById(R.id.customSurfaceViewOne1) and set the listener to it by cvo.setOnTouchListener(this);
then add your onTouch
#Override
public boolean onTouch(View v, MotionEvent event) {
customSurfaceViewOne1.myOnTouch(event);
return false;
}
where myOnTouch() is a method in your customSurfaceViewOne1 class where you will do all your ontTouch events. Notice I passed in the MotionEvent event.
Again, I'm not sure if this is the proper way to do it, it's just how I got it to work. The reason I did it was just so I could have an admob ad above my surfaceview lol.

Webview in Facebook SDK

I'm trying to integrate some Facebook stuff (posting on your own "wall") in my TabView.
The problem I'm facing is that while I do not have the Facebook app installed, I get a Webview showing the login and posting. It shows nicely, except for the buttons at the bottom. If I scroll down to see the buttons, they just go a little bit lower.
The layout of the tab:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="40dip">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/Test_Facebook_Layout">
<Button android:id="#+id/facebookButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Login to facebook"/>
</LinearLayout>
</ScrollView>
Got all the code (except the parts I changed and will post below) from here.
public void onClick(View v)
{
if (v == facebookButton)
{
facebookClient = new Facebook("myAwesomeKey");
facebookClient.authorize(this, new String[] {"publish_stream", "read_stream", "offline_access"}, this);
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//https://stackoverflow.com/questions/2953146/android-java-post-simple-text-to-facebook-wall
setContentView(R.layout.detailsfacebooktab);
facebookButton = (Button)this.findViewById(R.id.facebookButton);
facebookButton.setOnClickListener(this);
}
What would be nice is either a) Getting the WebView to not fill my screen or b) Keep the buttons from scrolling away.
EDIT: Because a picture says more than a thousend words, here's three thousand words.
(if it isn't clear, Picture 1 shows the initial view, Picture 2 shows my scrolling down, Picture 3 shows the buttons getting away)
For the moment I found a c) Getting the buttons a bit higher.
The problem is the user can still scroll the WebView, enlarging the View and scrolling the buttons back down again.
I edited these values in com.facebook.android.fbDialog.java to show the buttons a bit more.
static final float[] DIMENSIONS_LANDSCAPE = {440, 260};
static final float[] DIMENSIONS_PORTRAIT = {280, 380};
Because this does not completely solve my problem, I've edited my OP instead of posting my own "answer".
Thank you,
iarwain01

ViewFlipper crashes after orientation change

I'm trying to use inflate while using ViewFlipper to access to the data inside my view. I've done sample project that crashes.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/RelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
FlipViewBug.java
package android.FlipViewBug;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
public class FlipViewBug extends Activity {
private static LayoutInflater inflater = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
}
When I rotate emulator from horizontal to vertical orientation CTRL+F11 app crashes with stopped unexpectedly.
If I remove line
inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
app works fine. Am I trying to do something wrong? In my app I have more complex LinnarView that ViewFlipper is nested and the results are the same.
I was checking this on android 1.5, 2.2 and galaxy tab. There is no problem while rotating form vertical view to horizontal.
As a rule of thumb if you're storing UI objectrefs in static data you're probably doing something wrong. :) Even when things appear to work, you are likely to be leaking memory until Android decides to kill your process. See Romain Guy's article on this for more details.
So basically you answered your own question... don't do that! If you want to delay inflation of flipped-out views until they are flipped-in (i.e. as a performance improvement) I'd suggest you look into ViewStub.

surfaceview + glsurfaceview + framelayout

I'm new at java and OpenGL.
I'm trying to get a camera preview screen with the ability to
display 3d objects simultaneously. Having gone through the samples at
the api demos, I thought combining the code for the the examples at
the api demo would suffice. But somehow its not working. The forces me
to shut down upon startup and the error is mentioned as null pointer
exception. Could someone share with me where did I go wrong and how to
proceed from there. How I did the combination for the code is as shown
below:
myoverview.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.opengl.GLSurfaceView
android:id="#+id/cubes"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<SurfaceView
android:id="#+id/camera"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
myoverview.java
import android.app.Activity;
import android.os.Bundle;
import android.view.SurfaceView;
import android.view.Window;
public class MyOverView extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
// camera view as the background
SurfaceView cameraView = (SurfaceView) findViewById(R.id.camera);
cameraView = new CameraView(this);
// visual of both cubes
GLSurfaceView cubesView = (GLSurfaceView) findViewById(R.id.cubes);
cubesView = new GLSurfaceView(this);
cubesView.setRenderer(new CubeRenderer(false));
// set view
setContentView(R.layout.myoverview);
}
}
GLSurfaceView.java
import android.content.Context;
class GLSurfaceView extends android.opengl.GLSurfaceView {
public GLSurfaceView(Context context) {
super(context);
}
}
NOTE :
I didn't list the rest of the files as they are just copies of
the api demos. The cameraView refers to the camerapreview.java example
and the CubeRenderer refers to the CubeRenderer.java and Cube.java
example. Any help would be appreciated.
Sorry, didn't realize that the coding was out of place due to formatting mistakes.
the reason you are getting a null pointer exception when working with .xml is because ur actually creating new Views in your java code.. instead of using the ones from the .xml file to which you might have passed in properties(if u did pass in properties that is..).. the new View would obviously have a null value.. thus throwing a null pointer exception... for example --
cubesView = new GLSurfaceView(this);
is actually not needed in the code if you already created the View in the .xml file containing FrameLayout..
This is very simple actually...if you want to define your view in XML you just have to implement
Public GLSurfaceView(Context context, AttributeSet attrs) {
...
super(context, attrs);
}
instead of GLSurfaceView(Context context)
That's the one that gets called automatically when the view is initialized from the XML. I had the same problem and that's how it was fixed.
Found out how to solve it... via the java way... just use addContentView instead of using xml.... well at least its solved. :)
I actually did that here in this SO link which provides a complete implementation.

Categories

Resources