I started working on AndEngine and I have learnt a lot but i am stuck in creating levels selector for my game. I know this is something to do with getTextureManager() as i went through this when i am trying to attach ITextureRegion to my sprite object, that i solved but this time i am going nowhere for last day.
mainActivity.java
import org.andengine.engine.camera.Camera;
import org.andengine.engine.options.EngineOptions;
import org.andengine.engine.options.ScreenOrientation;
import org.andengine.engine.options.WakeLockOptions;
import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.opengl.font.FontFactory;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.andengine.opengl.texture.atlas.bitmap.BuildableBitmapTextureAtlas;
import org.andengine.opengl.texture.atlas.bitmap.source.IBitmapTextureAtlasSource;
import org.andengine.opengl.texture.atlas.buildable.builder.BlackPawnTextureAtlasBuilder;
import org.andengine.opengl.texture.atlas.buildable.builder.ITextureAtlasBuilder.TextureAtlasBuilderException;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.texture.region.TextureRegion;
import org.andengine.ui.activity.SimpleBaseGameActivity;
import org.andengine.util.color.Color;
import android.graphics.Typeface;
import android.util.Log;
public class Main_Activity extends SimpleBaseGameActivity {
EngineOptions engine;
Camera mCamera;
private static final int WIDTH = 800;
private static final int HEIGHT = 480;
LevelSelector ls;
Scene mScene;
public org.andengine.opengl.font.Font mFont;
ITextureRegion mTextureRegion;
#Override
public EngineOptions onCreateEngineOptions() {
mCamera = new Camera(0, 0, WIDTH, HEIGHT);
engine = new EngineOptions(true, ScreenOrientation.LANDSCAPE_FIXED, new FillResolutionPolicy(), mCamera);
engine.setWakeLockOptions(WakeLockOptions.SCREEN_ON);
return engine;
}
#Override
protected void onCreateResources() {
BuildableBitmapTextureAtlas mBitmapTextureAtlas = new BuildableBitmapTextureAtlas(this.getTextureManager() ,32 , 32);
mTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(mBitmapTextureAtlas, this, "marble.png");
try {
mBitmapTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 1, 1));
mBitmapTextureAtlas.load();
} catch (TextureAtlasBuilderException e) {
Log.e("", ""+e);
}
this.mFont = FontFactory.create(this.getFontManager(), this.getTextureManager(), 256, 256, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32);
this.mFont.load();
}
#Override
protected Scene onCreateScene() {
mScene = new Scene();
mScene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
ls = new LevelSelector(5, 2, WIDTH, HEIGHT, mScene, mEngine);
ls.createTiles(mTextureRegion, mFont);
ls.show();
return mScene;
}
}
LayerSelector.java
import org.andengine.engine.options.EngineOptions;
import org.andengine.entity.Entity;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.text.Text;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.texture.region.ITextureRegion;
import org.andengine.opengl.texture.region.TextureRegion;
import android.service.wallpaper.WallpaperService.Engine;
public class LevelSelector extends Entity {
/* Level selector layer properties */
private final int COLUMNS = 6;
private final int ROWS = 6;
/* Level selector tile properties */
private final int TILE_DIMENSION = 50;
private final int TILE_PADDING = 15;
private final Scene mScene;
private final org.andengine.engine.Engine mEngine;
/*
* The mChapter variable can allow each LevelSelector object to contain
* level tiles which begin levels in different chapters.
*/
private final int mChapter;
/* Variable containing the current max level unlocked */
private final int mMaxLevel;
/* Camera width and height are needed for the layout */
private final int mCameraWidth;
private final int mCameraHeight;
/* Initial x/y coordinates used for tile positioning */
private final float mInitialX;
private final float mInitialY;
/*
* Variable which defines whether the LevelSelector is hidden or visible
*/
private boolean mHidden = true;
/**
* The LevelSelector object can be used to display a grid of level tiles for
* user selection.
*
* #param pMaxLevel
* Current max unlocked level.
* #param pChapter
* Chapter/world number of this particular LevelSelector.
* #param pCameraWidth
* Camera object's width value.
* #param pCameraHeight
* Camera object's height value.
* #param pScene
* The Scene in which the LevelSelector will be displayed on.
* #param pEngine
* AndEngine's mEngine object.
*/
public LevelSelector(final int pMaxLevel, final int pChapter,
final int pCameraWidth, final int pCameraHeight,
final Scene pScene, final org.andengine.engine.Engine pEngine) {
/* Initialize member variables */
this.mScene = pScene;
this.mEngine = pEngine;
this.mChapter = pChapter;
this.mMaxLevel = pMaxLevel;
this.mCameraWidth = pCameraWidth;
this.mCameraHeight = pCameraHeight;
/*
* Obtain the initial tile's X coordinate by subtracting half of the
* entire level selector width including all tiles and padding from the
* center of the Scene
*/
final float halfLevelSelectorWidth = ((TILE_DIMENSION * COLUMNS) + TILE_PADDING
* (COLUMNS - 1)) * 0.5f;
this.mInitialX = (this.mCameraWidth * 0.5f) - halfLevelSelectorWidth;
/* Same math as above applies to the Y coordinate */
final float halfLevelSelectorHeight = ((TILE_DIMENSION * ROWS) + TILE_PADDING
* (ROWS - 1)) * 0.5f;
this.mInitialY = (this.mCameraHeight * 0.5f) + halfLevelSelectorHeight;
mEngine.getTextureManager();
}
/**
* Create the level tiles with a customized ITextureRegion representation as
* well as a customized Font.
*
* #param pTextureRegion
* The ITextureRegion to supply each of the level tiles.
* #param pFont
* The Font to be displayed by Text written on the tiles,
* specifying tile level number for example.
*/
public void createTiles(final ITextureRegion pTextureRegion,
final org.andengine.opengl.font.Font pFont) {
/* Temp coordinates for placing level tiles */
float tempX = this.mInitialX + TILE_DIMENSION * 0.5f;
float tempY = this.mInitialY - TILE_DIMENSION * 0.5f;
/* Current level of the tile to be placed */
int currentTileLevel = 1;
/*
* Loop through the Rows, adjusting tempY coordinate after each
* iteration
*/
for (int i = 0; i < ROWS; i++) {
/*
* Loop through the column positions, placing a LevelTile in each
* column
*/
for (int o = 0; o < COLUMNS; o++) {
final boolean locked;
/* Determine whether the current tile is locked or not */
if (currentTileLevel <= mMaxLevel) {
locked = false;
} else {
locked = true;
}
/* Create a level tile */
LevelTile levelTile = new LevelTile(tempX, tempY, locked,
currentTileLevel, pTextureRegion, pFont);
/*
* Attach the level tile's text based on the locked and
* currentTileLevel variables pass to its constructor
*/
levelTile.attachText();
/* Register & Attach the levelTile object to the LevelSelector */
mScene.registerTouchArea(levelTile);
this.attachChild(levelTile);
/* Increment the tempX coordinate to the next column */
tempX = tempX + TILE_DIMENSION + TILE_PADDING;
/* Increment the level tile count */
currentTileLevel++;
}
/* Reposition the tempX coordinate back to the first row (far left) */
tempX = mInitialX + TILE_DIMENSION * 0.5f;
/* Reposition the tempY coordinate for the next row to apply tiles */
tempY = tempY - TILE_DIMENSION - TILE_PADDING;
}
}
/**
* Display the LevelSelector on the Scene.
*/
public void show() {
/* Register as non-hidden, allowing touch events */
mHidden = false;
/* Attach the LevelSelector the the Scene if it currently has no parent */
if (!this.hasParent()) {
mScene.attachChild(this);
}
/* Set the LevelSelector to visible */
this.setVisible(true);
}
/**
* Hide the LevelSelector on the Scene.
*/
public void hide() {
/* Register as hidden, disallowing touch events */
mHidden = true;
/* Remove the LevelSelector from view */
this.setVisible(false);
}
public class LevelTile extends Sprite {
/*
* The LevelTile should keep track of level number and lock status. Feel
* free to add additional data within level tiles
*/
private final boolean mIsLocked;
private final int mLevelNumber;
private final org.andengine.opengl.font.Font mFont;
private Text mTileText;
/*
* Each level tile will be sized according to the constant
* TILE_DIMENSION within the LevelSelector class
*/
public LevelTile(float pX, float pY, boolean pIsLocked,
int pLevelNumber, ITextureRegion pTextureRegion, org.andengine.opengl.font.Font pFont) {
super(pX, pY, LevelSelector.this.TILE_DIMENSION,
LevelSelector.this.TILE_DIMENSION, pTextureRegion,
LevelSelector.this.mEngine.getVertexBufferObjectManager());
/* Initialize the necessary variables for the LevelTile */
this.mFont = pFont;
this.mIsLocked = pIsLocked;
this.mLevelNumber = pLevelNumber;
}
/* Method used to obtain whether or not this level tile represents a
* level which is currently locked */
public boolean isLocked() {
return this.mIsLocked;
}
/* Method used to obtain this specific level tiles level number */
public int getLevelNumber() {
return this.mLevelNumber;
}
/*
* Attach the LevelTile's text to itself based on whether it's locked or
* not. If not, then the level number will be displayed on the level
* tile.
*/
public void attachText() {
String tileTextString = null;
/* If the tile's text is currently null... */
if (this.mTileText == null) {
/*
* Determine the tile's string based on whether it's locked or
* not
*/
if (this.mIsLocked) {
tileTextString = "Locked";
} else {
tileTextString = String.valueOf(this.mLevelNumber);
}
/* Setup the text position to be placed in the center of the tile */
final float textPositionX = LevelSelector.this.TILE_DIMENSION * 0.5f;
final float textPositionY = textPositionX;
/* Create the tile's text in the center of the tile */
this.mTileText = new Text( textPositionX,
textPositionY, this.mFont,
tileTextString, tileTextString.length(),
LevelSelector.this.mEngine
.getVertexBufferObjectManager());
/* Attach the Text to the LevelTile */
this.attachChild(mTileText);
}
}
#Override
public boolean onAreaTouched(TouchEvent pSceneTouchEvent,
float pTouchAreaLocalX, float pTouchAreaLocalY) {
/* If the LevelSelector is not hidden, proceed to execute the touch
* event */
if (!LevelSelector.this.mHidden) {
/* If a level tile is initially pressed down on */
if (pSceneTouchEvent.isActionDown()) {
/* If this level tile is locked... */
if (this.mIsLocked) {
/* Tile Locked event... */
LevelSelector.this.mScene.getBackground().setColor(
org.andengine.util.color.Color.RED);
} else {
/* Tile unlocked event... This event would likely prompt
* level loading but without getting too complicated we
* will simply set the Scene's background color to green */
LevelSelector.this.mScene.getBackground().setColor(
org.andengine.util.color.Color.GREEN);
/**
* Example level loading:
* LevelSelector.this.hide();
* SceneManager.loadLevel(this.mLevelNumber);
*/
}
return true;
}
}
return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX,
pTouchAreaLocalY);
}
}
}
FYI, this code belongs to code bundle in Android game development using AndEngine cookbook.
I have edited few variables like font and engine , but i have't changed the logic or flow of program.
Now when I run this I get the below screen
Alright i solved my problem, its was the padding i was using, it should't be there.
try {
mBitmapTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 1, 1));
mBitmapTextureAtlas.load();
} catch (TextureAtlasBuilderException e) {
Log.e("", ""+e);
}
it should be
try {
mBitmapTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 0, 0));
mBitmapTextureAtlas.load();
} catch (TextureAtlasBuilderException e) {
Log.e("", ""+e);
}
Related
I want to learn how to handle the image and gesture function in android.
So I read the sample "InteractiveChart" under "Animating a Scroll Gesture" section in Android developer website.
While I read about "onDoubleTap" method in InteractiveLineGraphView.java.
#Override
public boolean onDoubleTap(MotionEvent e) {
mZoomer.forceFinished(true);
if (hitTest(e.getX(), e.getY(), mZoomFocalPoint)) {
mZoomer.startZoom(ZOOM_AMOUNT);
}
ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
return true;
}
I checked the code of Zoomer.
It mainly calls DecelerateInterpolator method and set some variables.
I wonder how can "Zoomer" achieve the double tap zoom function.
Does "DecelerateInterpolator" do the work? Or I just missed something?
public class Zoomer {
/**
* The interpolator, used for making zooms animate 'naturally.'
*/
private Interpolator mInterpolator;
/**
* The total animation duration for a zoom.
*/
private int mAnimationDurationMillis;
/**
* Whether or not the current zoom has finished.
*/
private boolean mFinished = true;
/**
* The current zoom value; computed by {#link #computeZoom()}.
*/
private float mCurrentZoom;
/**
* The time the zoom started, computed using {#link android.os.SystemClock#elapsedRealtime()}.
*/
private long mStartRTC;
/**
* The destination zoom factor.
*/
private float mEndZoom;
public Zoomer(Context context) {
mInterpolator = new DecelerateInterpolator();
mAnimationDurationMillis = context.getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
/**
* Forces the zoom finished state to the given value. Unlike {#link #abortAnimation()}, the
* current zoom value isn't set to the ending value.
*
* #see android.widget.Scroller#forceFinished(boolean)
*/
public void forceFinished(boolean finished) {
mFinished = finished;
}
/**
* Aborts the animation, setting the current zoom value to the ending value.
*
* #see android.widget.Scroller#abortAnimation()
*/
public void abortAnimation() {
mFinished = true;
mCurrentZoom = mEndZoom;
}
/**
* Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should
* by 0.25f.
*
* #see android.widget.Scroller#startScroll(int, int, int, int)
*/
public void startZoom(float endZoom) {
mStartRTC = SystemClock.elapsedRealtime();
mEndZoom = endZoom;
mFinished = false;
mCurrentZoom = 1f;
}
/**
* Computes the current zoom level, returning true if the zoom is still active and false if the
* zoom has finished.
*
* #see android.widget.Scroller#computeScrollOffset()
*/
public boolean computeZoom() {
if (mFinished) {
return false;
}
long tRTC = SystemClock.elapsedRealtime() - mStartRTC;
if (tRTC >= mAnimationDurationMillis) {
mFinished = true;
mCurrentZoom = mEndZoom;
return false;
}
float t = tRTC * 1f / mAnimationDurationMillis;
mCurrentZoom = mEndZoom * mInterpolator.getInterpolation(t);
return true;
}
/**
* Returns the current zoom level.
*
* #see android.widget.Scroller#getCurrX()
*/
public float getCurrZoom() {
return mCurrentZoom;
}
}
Can someone also recommend some great sample about image and gesture handling? From basic to advanced.....Thanks a lot.
The double tap is actually done in InteractiveLineGraphView view itself
#Override
public boolean onDoubleTap(MotionEvent e) {
mZoomer.forceFinished(true);
if (hitTest(e.getX(), e.getY(), mZoomFocalPoint)) {
mZoomer.startZoom(ZOOM_AMOUNT);
}
ViewCompat.postInvalidateOnAnimation(InteractiveLineGraphView.this);
return true;
}
Zoomer is just a helper class holding current zoom level
I'm developing an Unity-Android Plugin to record game screen and create a mp4 video file.I follow to Android Breakout game recorder patch sample in this site : http://bigflake.com/mediacodec/.
First, I create my CustomUnityPlayer class that extends UnityPlayer class and override onDrawFrame method.Here is my CustomUnityPlayer class code :
package com.example.screenrecorder;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.ContextWrapper;
import android.opengl.EGL14;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import com.unity3d.player.*;
public class CustomUnityPlayer extends UnityPlayer implements GLSurfaceView.Renderer {
public static final String TAG = "ScreenRecord";
public static final boolean EXTRA_CHECK = true; // enable additional assertions
private GameRecorder recorder;
static final float mProjectionMatrix[] = new float[16];
private final float mSavedMatrix[] = new float[16];
private EGLDisplay mSavedEglDisplay;
private EGLSurface mSavedEglDrawSurface;
private EGLSurface mSavedEglReadSurface;
private EGLContext mSavedEglContext;
// Frame counter, used for reducing recorder frame rate.
private int mFrameCount;
static final float ARENA_WIDTH = 768.0f;
static final float ARENA_HEIGHT = 1024.0f;
private int mViewportWidth, mViewportHeight;
private int mViewportXoff, mViewportYoff;
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private float mAngle;
public CustomUnityPlayer(ContextWrapper context) {
// TODO Auto-generated constructor stub
super(context);
this.recorder = GameRecorder.getInstance();
}
private boolean recordThisFrame() {
final int TARGET_FPS = 30;
mFrameCount ++;
switch (TARGET_FPS) {
case 60:
return true;
case 30:
return (mFrameCount & 0x01) == 0;
case 24:
// want 2 out of every 5 frames
int mod = mFrameCount % 5;
return mod == 0 || mod == 2;
default:
return true;
}
}
public void onDrawFrame(GL10 gl){
//record this frame
if (this.recorder.isRecording() && this.recordThisFrame()) {
saveRenderState();
// switch to recorder state
this.recorder.makeCurrent();
super.onDrawFrame(gl);
this.recorder.getProjectionMatrix(mProjectionMatrix);
this.recorder.setViewport();
this.recorder.swapBuffers();
restoreRenderState();
}
}
public void onSurfaceCreated(GL10 paramGL10, EGLConfig paramEGLConfig){
// now repeat it for the game recorder
if (this.recorder.isRecording()) {
Log.d(TAG, "configuring GL for recorder");
saveRenderState();
this.recorder.firstTimeSetup();
super.onSurfaceCreated(paramGL10, paramEGLConfig);
this.recorder.makeCurrent();
//glSetup();
restoreRenderState();
mFrameCount = 0;
}
if (EXTRA_CHECK) Util.checkGlError("onSurfaceCreated end");
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
/*
* We want the viewport to be proportional to the arena size. That way a 10x10
* object in arena coordinates will look square on the screen, and our round ball
* will look round.
*
* If we wanted to fill the entire screen with our game, we would want to adjust the
* size of the arena itself, not just stretch it to fit the boundaries. This can have
* subtle effects on gameplay, e.g. the time it takes the ball to travel from the top
* to the bottom of the screen will be different on a device with a 16:9 display than on
* a 4:3 display. Other games might address this differently, e.g. a side-scroller
* could display a bit more of the level on the left and right.
*
* We do want to fill as much space as we can, so we should either be pressed up against
* the left/right edges or top/bottom.
*
* Our game plays best in portrait mode. We could force the app to run in portrait
* mode (by setting a value in AndroidManifest, or by setting the projection to rotate
* the world to match the longest screen dimension), but that's annoying, especially
* on devices that don't rotate easily (e.g. plasma TVs).
*/
super.onSurfaceChanged(unused, width, height);
if (EXTRA_CHECK) Util.checkGlError("onSurfaceChanged start");
float arenaRatio = ARENA_HEIGHT / ARENA_WIDTH;
int x, y, viewWidth, viewHeight;
if (height > (int) (width * arenaRatio)) {
// limited by narrow width; restrict height
viewWidth = width;
viewHeight = (int) (width * arenaRatio);
} else {
// limited by short height; restrict width
viewHeight = height;
viewWidth = (int) (height / arenaRatio);
}
x = (width - viewWidth) / 2;
y = (height - viewHeight) / 2;
Log.d(TAG, "onSurfaceChanged w=" + width + " h=" + height);
Log.d(TAG, " --> x=" + x + " y=" + y + " gw=" + viewWidth + " gh=" + viewHeight);
GLES20.glViewport(x, y, viewWidth, viewHeight);
mViewportXoff = x;
mViewportYoff = y;
mViewportWidth = viewWidth;
mViewportHeight = viewHeight;
// Create an orthographic projection that maps the desired arena size to the viewport
// dimensions.
//
// If we reversed {0, ARENA_HEIGHT} to {ARENA_HEIGHT, 0}, we'd have (0,0) in the
// upper-left corner instead of the bottom left, which is more familiar for 2D
// graphics work. It might cause brain ache if we want to mix in 3D elements though.
Matrix.orthoM(mProjectionMatrix, 0, 0, ARENA_WIDTH,
0, ARENA_HEIGHT, -1, 1);
Log.d(TAG, "onSurfaceChangedEnd 1 w=" + width + " h=" + height);
if (EXTRA_CHECK) Util.checkGlError("onSurfaceChanged end");
Log.d(TAG, "onSurfaceEnded w=" + width + " h=" + height);
}
public void pause(){
super.pause();
this.recorder.gamePaused();
}
/**
* Saves the current projection matrix and EGL state.
*/
public void saveRenderState() {
System.arraycopy(mProjectionMatrix, 0, mSavedMatrix, 0, mProjectionMatrix.length);
mSavedEglDisplay = EGL14.eglGetCurrentDisplay();
mSavedEglDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
mSavedEglReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ);
mSavedEglContext = EGL14.eglGetCurrentContext();
}
/**
* Saves the current projection matrix and EGL state.
*/
public void restoreRenderState() {
// switch back to previous state
if (!EGL14.eglMakeCurrent(mSavedEglDisplay, mSavedEglDrawSurface, mSavedEglReadSurface,
mSavedEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
System.arraycopy(mSavedMatrix, 0, mProjectionMatrix, 0, mProjectionMatrix.length);
}
}
And then, i create a CustomUnityPlayerActivity to call this class
package com.example.screenrecorder;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import com.unity3d.player.UnityPlayerActivity;
public class CustomUnityActivity extends UnityPlayerActivity {
private CustomUnityPlayer mUnityPlayer;
private GameRecorder mRecorder;
#Override
protected void onCreate(Bundle paramBundle){
Log.e("ScreenRecord","oncreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(paramBundle);
this.mUnityPlayer = new CustomUnityPlayer(this);
if (this.mUnityPlayer.getSettings().getBoolean("hide_status_bar", true))
getWindow().setFlags(1024, 1024);
int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
boolean trueColor8888 = false;
mUnityPlayer.init(glesMode, trueColor8888);
View playerView = mUnityPlayer.getView();
setContentView(playerView);
playerView.requestFocus();
this.mRecorder = GameRecorder.getInstance();
this.mRecorder.prepareEncoder(this);
}
public void beginRecord(){
Log.e("ScreenRecord","start record");
this.mUnityPlayer.saveRenderState();
this.mRecorder.firstTimeSetup();
this.mRecorder.setStartRecord(true);
this.mRecorder.makeCurrent();
this.mUnityPlayer.restoreRenderState();
}
public void endRecord(){
Log.e("ScreenRecord","end record");
this.mRecorder.endRecord();
this.mRecorder.setStartRecord(false);
//this.mTransView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
public boolean isRecording(){
return this.mRecorder.isRecording();
}
protected void onDestroy()
{
super.onDestroy();
this.mUnityPlayer.quit();
}
protected void onPause()
{
super.onPause();
this.mUnityPlayer.pause();
}
protected void onResume()
{
super.onResume();
this.mUnityPlayer.resume();
}
public void onConfigurationChanged(Configuration paramConfiguration)
{
super.onConfigurationChanged(paramConfiguration);
this.mUnityPlayer.configurationChanged(paramConfiguration);
}
public void onWindowFocusChanged(boolean paramBoolean)
{
super.onWindowFocusChanged(paramBoolean);
this.mUnityPlayer.windowFocusChanged(paramBoolean);
}
public boolean onKeyDown(int paramInt, KeyEvent paramKeyEvent)
{
return this.mUnityPlayer.onKeyDown(paramInt, paramKeyEvent);
}
public boolean onKeyUp(int paramInt, KeyEvent paramKeyEvent)
{
return this.mUnityPlayer.onKeyUp(paramInt, paramKeyEvent);
}
}
My problem is that a video file is created successful but my game can't render anything.I read on Android Media Codec sample site and recognize that each frame would be render twice (once for the display, once for the video) but I can't do this in Unity.Whenever I try to call super.onDrawFrame(gl) twice in onDrawFrame method , my game will be crashed.
Any solution for my problem? Any help will be greatly appreciated!
Thanks and best regard!
Huy Tran
Finally I use a FrameBufferObject (FBO) to render offscreen and get its binding texture to blit twice:
Render to video surface
Redraw to device screen
You can find more detail about this solution by reference to my another question use FBO to record Unity gamescreen
Kamcord plug-in can help you : http://www.kamcord.com/
Is it possible to set an Icon in ActionBar through setIcon method (that accept drawable or Resource id) with a "Text" using AwesomeFont?
Should I create a Drawable or something like that? In Drawable what should I Create?
EDIT
public class AwesomeTextView extends TextView {
private static Typeface fontAwesome;
public AwesomeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
if (fontAwesome == null) {
fontAwesome = Typeface.createFromAsset(context.getAssets(), "fonts/fontawesome-webfont.ttf");
}
setTypeface(fontAwesome);
}
}
So I had created an AwesomeTextView that uses font-awesome, Is there a way to make an AwesomeTextView as drawable?
I made a repo to easily add font awesome icons to your android project
https://github.com/bperin/FontAwesomeAndroid
If you're trying to add these in an action bar you'll probably need to make a custom layout via XML
Yes, you need to create a Drawable and draw the icon. You could take a look at the TextDrawable project on github. You can modify it such that Font-Awesome is used as the typeface.
Make sure that you bundle the font along with the project.
Here's a complete guide on how to set Icon with FontAwesome with setIcon() method.
http://blog.incognitech.in/font-awesome-in-android
UPDATE:
FontManager Class
public class FontAwesomeManager {
public static final String ROOT = "fonts/";
public static final String FONTAWESOME = ROOT + "fontawesome-webfont.ttf";
public static Typeface getTypeface(Context context, String font) {
return Typeface.createFromAsset(context.getAssets(), font);
}
}
TextDrawable class
package in.incognitech.reminder.util;
/**
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.TypedValue;
/**
* A Drawable object that draws text.
* A TextDrawable accepts most of the same parameters that can be applied to
* {#link android.widget.TextView} for displaying and formatting text.
*
* Optionally, a {#link Path} may be supplied on which to draw the text.
*
* A TextDrawable has an intrinsic size equal to that required to draw all
* the text it has been supplied, when possible. In cases where a {#link Path}
* has been supplied, the caller must explicitly call
* {#link #setBounds(android.graphics.Rect) setBounds()} to provide the Drawable
* size based on the Path constraints.
*/
public class TextDrawable extends Drawable {
/* Platform XML constants for typeface */
private static final int SANS = 1;
private static final int SERIF = 2;
private static final int MONOSPACE = 3;
/* Resources for scaling values to the given device */
private Resources mResources;
/* Paint to hold most drawing primitives for the text */
private TextPaint mTextPaint;
/* Layout is used to measure and draw the text */
private StaticLayout mTextLayout;
/* Alignment of the text inside its bounds */
private Layout.Alignment mTextAlignment = Layout.Alignment.ALIGN_NORMAL;
/* Optional path on which to draw the text */
private Path mTextPath;
/* Stateful text color list */
private ColorStateList mTextColors;
/* Container for the bounds to be reported to widgets */
private Rect mTextBounds;
/* Text string to draw */
private CharSequence mText = "";
/* Attribute lists to pull default values from the current theme */
private static final int[] themeAttributes = {
android.R.attr.textAppearance
};
private static final int[] appearanceAttributes = {
android.R.attr.textSize,
android.R.attr.typeface,
android.R.attr.textStyle,
android.R.attr.textColor
};
public TextDrawable(Context context) {
super();
//Used to load and scale resource items
mResources = context.getResources();
//Definition of this drawables size
mTextBounds = new Rect();
//Paint to use for the text
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.density = mResources.getDisplayMetrics().density;
mTextPaint.setDither(true);
int textSize = 15;
ColorStateList textColor = null;
int styleIndex = -1;
int typefaceIndex = -1;
//Set default parameters from the current theme
TypedArray a = context.getTheme().obtainStyledAttributes(themeAttributes);
int appearanceId = a.getResourceId(0, -1);
a.recycle();
TypedArray ap = null;
if (appearanceId != -1) {
ap = context.obtainStyledAttributes(appearanceId, appearanceAttributes);
}
if (ap != null) {
for (int i=0; i < ap.getIndexCount(); i++) {
int attr = ap.getIndex(i);
switch (attr) {
case 0: //Text Size
textSize = a.getDimensionPixelSize(attr, textSize);
break;
case 1: //Typeface
typefaceIndex = a.getInt(attr, typefaceIndex);
break;
case 2: //Text Style
styleIndex = a.getInt(attr, styleIndex);
break;
case 3: //Text Color
textColor = a.getColorStateList(attr);
break;
default:
break;
}
}
ap.recycle();
}
setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
setRawTextSize(textSize);
Typeface tf = null;
switch (typefaceIndex) {
case SANS:
tf = Typeface.SANS_SERIF;
break;
case SERIF:
tf = Typeface.SERIF;
break;
case MONOSPACE:
tf = Typeface.MONOSPACE;
break;
}
setTypeface(tf, styleIndex);
}
/**
* Set the text that will be displayed
* #param text Text to display
*/
public void setText(CharSequence text) {
if (text == null) text = "";
mText = text;
measureContent();
}
/**
* Return the text currently being displayed
*/
public CharSequence getText() {
return mText;
}
/**
* Return the current text size, in pixels
*/
public float getTextSize() {
return mTextPaint.getTextSize();
}
/**
* Set the text size. The value will be interpreted in "sp" units
* #param size Text size value, in sp
*/
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the text size, using the supplied complex units
* #param unit Units for the text size, such as dp or sp
* #param size Text size value
*/
public void setTextSize(int unit, float size) {
float dimension = TypedValue.applyDimension(unit, size,
mResources.getDisplayMetrics());
setRawTextSize(dimension);
}
/*
* Set the text size, in raw pixels
*/
private void setRawTextSize(float size) {
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
measureContent();
}
}
/**
* Return the horizontal stretch factor of the text
*/
public float getTextScaleX() {
return mTextPaint.getTextScaleX();
}
/**
* Set the horizontal stretch factor of the text
* #param size Text scale factor
*/
public void setTextScaleX(float size) {
if (size != mTextPaint.getTextScaleX()) {
mTextPaint.setTextScaleX(size);
measureContent();
}
}
/**
* Return the current text alignment setting
*/
public Layout.Alignment getTextAlign() {
return mTextAlignment;
}
/**
* Set the text alignment. The alignment itself is based on the text layout direction.
* For LTR text NORMAL is left aligned and OPPOSITE is right aligned.
* For RTL text, those alignments are reversed.
* #param align Text alignment value. Should be set to one of:
*
* {#link Layout.Alignment#ALIGN_NORMAL},
* {#link Layout.Alignment#ALIGN_NORMAL},
* {#link Layout.Alignment#ALIGN_OPPOSITE}.
*/
public void setTextAlign(Layout.Alignment align) {
if (mTextAlignment != align) {
mTextAlignment = align;
measureContent();
}
}
/**
* Sets the typeface and style in which the text should be displayed.
* Note that not all Typeface families actually have bold and italic
* variants, so you may need to use
* {#link #setTypeface(Typeface, int)} to get the appearance
* that you actually want.
*/
public void setTypeface(Typeface tf) {
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
measureContent();
}
}
/**
* Sets the typeface and style in which the text should be displayed,
* and turns on the fake bold and italic bits in the Paint if the
* Typeface that you provided does not have all the bits in the
* style that you specified.
*
*/
public void setTypeface(Typeface tf, int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
/**
* Return the current typeface and style that the Paint
* using for display.
*/
public Typeface getTypeface() {
return mTextPaint.getTypeface();
}
/**
* Set a single text color for all states
* #param color Color value such as {#link Color#WHITE} or {#link Color#argb(int, int, int, int)}
*/
public void setTextColor(int color) {
setTextColor(ColorStateList.valueOf(color));
}
/**
* Set the text color as a state list
* #param colorStateList ColorStateList of text colors, such as inflated from an R.color resource
*/
public void setTextColor(ColorStateList colorStateList) {
mTextColors = colorStateList;
updateTextColors(getState());
}
/**
* Optional Path object on which to draw the text. If this is set,
* TextDrawable cannot properly measure the bounds this drawable will need.
* You must call {#link #setBounds(int, int, int, int) setBounds()} before
* applying this TextDrawable to any View.
*
* Calling this method with <code>null</code> will remove any Path currently attached.
*/
public void setTextPath(Path path) {
if (mTextPath != path) {
mTextPath = path;
measureContent();
}
}
/**
* Internal method to take measurements of the current contents and apply
* the correct bounds when possible.
*/
private void measureContent() {
//If drawing to a path, we cannot measure intrinsic bounds
//We must resly on setBounds being called externally
if (mTextPath != null) {
//Clear any previous measurement
mTextLayout = null;
mTextBounds.setEmpty();
} else {
//Measure text bounds
double desired = Math.ceil( Layout.getDesiredWidth(mText, mTextPaint) );
mTextLayout = new StaticLayout(mText, mTextPaint, (int)desired,
mTextAlignment, 1.0f, 0.0f, false);
mTextBounds.set(0, 0, mTextLayout.getWidth(), mTextLayout.getHeight());
}
//We may need to be redrawn
invalidateSelf();
}
/**
* Internal method to apply the correct text color based on the drawable's state
*/
private boolean updateTextColors(int[] stateSet) {
int newColor = mTextColors.getColorForState(stateSet, Color.WHITE);
if (mTextPaint.getColor() != newColor) {
mTextPaint.setColor(newColor);
return true;
}
return false;
}
#Override
protected void onBoundsChange(Rect bounds) {
//Update the internal bounds in response to any external requests
mTextBounds.set(bounds);
}
#Override
public boolean isStateful() {
/*
* The drawable's ability to represent state is based on
* the text color list set
*/
return mTextColors.isStateful();
}
#Override
protected boolean onStateChange(int[] state) {
//Upon state changes, grab the correct text color
return updateTextColors(state);
}
#Override
public int getIntrinsicHeight() {
//Return the vertical bounds measured, or -1 if none
if (mTextBounds.isEmpty()) {
return -1;
} else {
return (mTextBounds.bottom - mTextBounds.top);
}
}
#Override
public int getIntrinsicWidth() {
//Return the horizontal bounds measured, or -1 if none
if (mTextBounds.isEmpty()) {
return -1;
} else {
return (mTextBounds.right - mTextBounds.left);
}
}
#Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
final int count = canvas.save();
canvas.translate(bounds.left, bounds.top);
if (mTextPath == null) {
//Allow the layout to draw the text
mTextLayout.draw(canvas);
} else {
//Draw directly on the canvas using the supplied path
canvas.drawTextOnPath(mText.toString(), mTextPath, 0, 0, mTextPaint);
}
canvas.restoreToCount(count);
}
#Override
public void setAlpha(int alpha) {
if (mTextPaint.getAlpha() != alpha) {
mTextPaint.setAlpha(alpha);
}
}
#Override
public int getOpacity() {
return mTextPaint.getAlpha();
}
#Override
public void setColorFilter(ColorFilter cf) {
if (mTextPaint.getColorFilter() != cf) {
mTextPaint.setColorFilter(cf);
}
}
}
Use FontManager & TextDrawable class
// ...
TextDrawable faIcon = new TextDrawable(this);
faIcon.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
faIcon.setTextAlign(Layout.Alignment.ALIGN_CENTER);
faIcon.setTypeface(FontAwesomeManager.getTypeface(this, FontAwesomeManager.FONTAWESOME));
faIcon.setText(getResources().getText(R.string.fa_android));
getActionBar().setIcon(faIcon);
// ...
this is quite an elaborate problem for me that i've been trying to figure out for a while now. Even explaining it is a little difficult for me but i'll give it a try.
I am using a gutted version of the Snake Android sample. Pretty much I am using the TileView class verbatim and am only trying to display a few tiles on the screen. Instead of the SnakeView class I am using a GameView class in which I only included the code I thought necessary to display a tile on the screen. Here is the class
public class GameView extends TileView {
/**
* Labels for the drawables that will be loaded into the TileView class
*/
private static final int RED_STAR = 1;
private static final int YELLOW_STAR = 2;
private static final int GREEN_STAR = 3;
/**
* mMoveDelay: number of milliseconds between animations.
*/
private long mMoveDelay = 600;
/**
* mLastMove: tracks the absolute time when the last animation happened.
*/
private long mLastMove;
/**
* Create a simple handler that we can use to cause animation to happen. We
* set ourselves as a target and we can use the sleep()
* function to cause an update/invalidate to occur at a later date.
*/
private RefreshHandler mRedrawHandler = new RefreshHandler();
class RefreshHandler extends Handler {
#Override
public void handleMessage(Message msg) {
GameView.this.update();
GameView.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a GameView based on inflation from XML
*
* #param context
* #param attrs
*/
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
initGameView();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGameView();
}
private void initGameView() {
setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
}
public void initNewGame() {
// set the move delay. This tells the update method how often it should
// refresh the screen.
mMoveDelay = 600;
update();
}
public void update() {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
// clear any tiles on the screen
clearTiles();
updateWalls();
// draws the tiles storred in mCellularArray
//updateCellularArray();
mLastMove = now;
}
mRedrawHandler.sleep(mMoveDelay);
}
private void updateWalls() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, x, 0);
setTile(GREEN_STAR, x, mYTileCount - 1);
}
for (int y = 1; y < mYTileCount - 1; y++) {
setTile(GREEN_STAR, 0, y);
setTile(GREEN_STAR, mXTileCount - 1, y);
}
}
private void updateCellularArray() {
setTile(YELLOW_STAR, 6, 7);
}
}
Now what is happening is that when the updateWalls() method is called, the tiles are placed within the View and comes up like this:
Now when I uncomment out the updateCellularArray() method and comment out the updateWalls() the program force closes and throws a NullPointerException. After some debugging I figured out that when the updateCellular array method is called, the mTileGrid array in the TileView class is not initialized, but it is when updateWalls() is called. I am completely baffled as to why this is happening. It doesn't matter if i replace the for loop with a simple setTile(GREEN_STAR, 3, 3); it still force closes.
Here is the TileView class I am using (again this is the same one in the Snake sample that comes with the Android SDK):
/**
* TileView: a View-variant designed for handling arrays of "icons" or other
* drawables.
*
*/
public class TileView extends View {
/**
* Parameters controlling the size of the tiles and their range within view.
* Width/Height are in pixels, and Drawables will be scaled to fit to these
* dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
*/
protected static int mTileSize;
protected static int mXTileCount;
protected static int mYTileCount;
private static int mXOffset;
private static int mYOffset;
/**
* A hash that maps integer handles specified by the subclasser to the
* drawable that will be used for that reference
*/
private Bitmap[] mTileArray;
/**
* A two-dimensional array of integers in which the number represents the
* index of the tile that should be drawn at that locations
*/
private int[][] mTileGrid;
private final Paint mPaint = new Paint();
public TileView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
public TileView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
/**
* Rests the internal array of Bitmaps used for drawing tiles, and
* sets the maximum index of tiles to be inserted
*
* #param tilecount
*/
public void resetTiles(int tilecount) {
mTileArray = new Bitmap[tilecount];
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mXTileCount = (int) Math.floor(w / mTileSize);
mYTileCount = (int) Math.floor(h / mTileSize);
mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
Log.d("Tomek", "TileGrid array dimensions are: " + mXTileCount + "," + mYTileCount);
mTileGrid = new int[mXTileCount][mYTileCount];
clearTiles();
}
/**
* Function to set the specified Drawable as the tile for a particular
* integer key.
*
* #param key
* #param tile
*/
public void loadTile(int key, Drawable tile) {
Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
tile.setBounds(0, 0, mTileSize, mTileSize);
tile.draw(canvas);
mTileArray[key] = bitmap;
}
/**
* Resets all tiles to 0 (empty)
*
*/
public void clearTiles() {
for (int x = 0; x < mXTileCount; x++) {
for (int y = 0; y < mYTileCount; y++) {
setTile(0, x, y);
}
}
}
/**
* Used to indicate that a particular tile (set with loadTile and referenced
* by an integer) should be drawn at the given x/y coordinates during the
* next invalidate/draw cycle.
*
* #param tileindex
* #param x
* #param y
*/
public void setTile(int tileindex, int x, int y) {
//Log.d("Tomek", "Attempting to set tile: " + x + "," + y);
//Log.d("Tomek", "The current value at " + x + "," + y + " is " + mTileGrid[x][y]);
//Log.d("Tomek", "It will be changed to " + tileindex);
mTileGrid[x][y] = tileindex;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int x = 0; x < mXTileCount; x += 1) {
for (int y = 0; y < mYTileCount; y += 1) {
if (mTileGrid[x][y] > 0) {
canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
mXOffset + x * mTileSize,
mYOffset + y * mTileSize,
mPaint);
}
}
}
}
}
Sorry for posting all this code. Any help would be greatly appreciated.
Thanks much,
Tomek
EDIT: simplified GameView class
EDIT 2: Alright after modifying the updateCellularArray() method to the following:
private void updateCellularArray() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, 12, 12);
}
}
It finally placed the tile where I wanted it to...
I'm starting to wonder if it has something to do with the RefreshHandler. Possibly when the RedrawHandler's sleep method is called in the update method of the GameView class. I'm not quite sure how it works so I am going to try playing around with it and see what I can come up with.
I forgot to instantiate the mLastMove variable. Modifying the declaration to:
private long mLastMove = System.currentTimeMillis();
fixed the problem.
Also it seems that the method used with the RefreshHandler, although clever, is outdated? Found that out with this post:
http://www.mail-archive.com/android-beginners#googlegroups.com/msg07352.html
Tomek
A problem came up in my latest android programming project.
The problem is I would like to change Activity that launches when the phone receives a call.
Is it possible to add some text after the contact name when a call is received.
I have search the web for something that could do that, and been looking in the API for hours and I cannot find anything, is it possible with reflection of something like that?
I have made a class that listens to when the phone_state is receiving a call, and I can get the incomming number, but I would like to change the appearance on the screen.
// Thanks in advance
you can,t edit InCallScreen interface. but you can display some Text or whatever above it using toast,
class MyToast ............
package i4nc4mp.myLock.phone;
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.app.INotificationManager;
import android.app.ITransientNotification;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.widget.TextView;
/**
* A toast is a view containing a quick little message for the user. The toast class
* helps you create and show those.
* {#more}
*
* <p>
* When the view is shown to the user, appears as a floating view over the
* application. It will never receive focus. The user will probably be in the
* middle of typing something else. The idea is to be as unobtrusive as
* possible, while still showing the user the information you want them to see.
* Two examples are the volume control, and the brief message saying that your
* settings have been saved.
* <p>
* The easiest way to use this class is to call one of the static methods that constructs
* everything you need and returns a new Toast object.
*/
public class MyToast {
static final String TAG = "Toast";
static final boolean localLOGV = false;
/**
* Show the view or text notification for a short period of time. This time
* could be user-definable. This is the default.
* #see #setDuration
*/
public static final int LENGTH_SHORT = 0;
/**
* Show the view or text notification for a long period of time. This time
* could be user-definable.
* #see #setDuration
*/
public static final int LENGTH_LONG = 1;
final Handler mHandler = new Handler();
final Context mContext;
final TN mTN;
int mDuration;
int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
int mX, mY;
float mHorizontalMargin;
float mVerticalMargin;
View mView;
View mNextView;
/**
* Construct an empty Toast object. You must call {#link #setView} before you
* can call {#link #show}.
*
* #param context The context to use. Usually your {#link android.app.Application}
* or {#link android.app.Activity} object.
*/
public MyToast(Context context) {
mContext = context;
mTN = new TN();
mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
}
/**
* Show the view for the specified duration.
*/
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getPackageName();
TN tn = mTN;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
/**
* Close the view if it's showing, or don't show it if it isn't showing yet.
* You do not normally have to call this. Normally view will disappear on its own
* after the appropriate duration.
*/
public void cancel() {
mTN.myHide();
// TODO this still needs to cancel the inflight notification if any
}
/**
* Set the view to show.
* #see #getView
*/
public void setView(View view) {
mNextView = view;
}
/**
* Return the view.
* #see #setView
*/
public View getView() {
return mNextView;
}
/**
* Set how long to show the view for.
* #see #LENGTH_SHORT
* #see #LENGTH_LONG
*/
public void setDuration(int duration) {
mDuration = duration;
}
/**
* Return the duration.
* #see #setDuration
*/
public int getDuration() {
return mDuration;
}
/**
* Set the margins of the view.
*
* #param horizontalMargin The horizontal margin, in percentage of the
* container width, between the container's edges and the
* notification
* #param verticalMargin The vertical margin, in percentage of the
* container height, between the container's edges and the
* notification
*/
public void setMargin(float horizontalMargin, float verticalMargin) {
mHorizontalMargin = horizontalMargin;
mVerticalMargin = verticalMargin;
}
/**
* Return the horizontal margin.
*/
public float getHorizontalMargin() {
return mHorizontalMargin;
}
/**
* Return the vertical margin.
*/
public float getVerticalMargin() {
return mVerticalMargin;
}
/**
* Set the location at which the notification should appear on the screen.
* #see android.view.Gravity
* #see #getGravity
*/
public void setGravity(int gravity, int xOffset, int yOffset) {
mGravity = gravity;
mX = xOffset;
mY = yOffset;
}
/**
* Get the location at which the notification should appear on the screen.
* #see android.view.Gravity
* #see #getGravity
*/
public int getGravity() {
return mGravity;
}
/**
* Return the X offset in pixels to apply to the gravity's location.
*/
public int getXOffset() {
return mX;
}
/**
* Return the Y offset in pixels to apply to the gravity's location.
*/
public int getYOffset() {
return mY;
}
/**
* Make a standard toast that just contains a text view.
*
* #param context The context to use. Usually your {#link android.app.Application}
* or {#link android.app.Activity} object.
* #param text The text to show. Can be formatted text.
* #param duration How long to display the message. Either {#link #LENGTH_SHORT} or
* {#link #LENGTH_LONG}
*
*/
public static MyToast makeText(Context context, CharSequence text, int duration) {
MyToast result = new MyToast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
/**
* Make a standard toast that just contains a text view with the text from a resource.
*
* #param context The context to use. Usually your {#link android.app.Application}
* or {#link android.app.Activity} object.
* #param resId The resource id of the string resource to use. Can be formatted text.
* #param duration How long to display the message. Either {#link #LENGTH_SHORT} or
* {#link #LENGTH_LONG}
*
* #throws Resources.NotFoundException if the resource can't be found.
*/
public static MyToast makeText(Context context, int resId, int duration)
throws Resources.NotFoundException {
return makeText(context, context.getResources().getText(resId), duration);
}
/**
* Update the text in a Toast that was previously created using one of the makeText() methods.
* #param resId The new text for the Toast.
*/
public void setText(int resId) {
setText(mContext.getText(resId));
}
/**
* Update the text in a Toast that was previously created using one of the makeText() methods.
* #param s The new text for the Toast.
*/
public void setText(CharSequence s) {
if (mNextView == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
if (tv == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
tv.setText(s);
}
// =======================================================================================
// All the gunk below is the interaction with the Notification Service, which handles
// the proper ordering of these system-wide.
// =======================================================================================
private static INotificationManager sService;
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}
private class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
public void run() {
handleHide();
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
WindowManagerImpl mWM;
TN() {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Theme_Dialog_Alert;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
}
/**
* schedule handleShow into the right thread
*/
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
/**
* schedule handleHide into the right thread
*/
public void hide() {
System.out.println("hide called");
if (localLOGV) Log.v(TAG, "HIDE: " + this);
// mHandler.post(mHide);
}
public void myHide(){
System.out.println("my hide called");
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
public void handleShow() {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
mWM = WindowManagerImpl.getDefault();
final int gravity = mGravity;
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
if (mView.getParent() != null) {
if (localLOGV) Log.v(
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
mWM.addView(mView, mParams);
}
}
public void handleHide() {
//System.out.println("handle hid ecalles");
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (mView.getParent() != null) {
if (localLOGV) Log.v(
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
mView = null;
}
}
}
}
// in broadcaste receiver ..........
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
toast = new MyToast(context);
toast.setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0);
toast.setDuration(Toast.LENGTH_SHORT);
toast.setView(view);
toast.show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
toast.cancel();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
toast.cancel();
}
}