[Android] Draw notification messages instead of using toaster

This commit is contained in:
zilmar 2016-09-17 11:43:32 +10:00
parent 49dcce96e9
commit 9b1587992e
6 changed files with 157 additions and 128 deletions

View File

@ -1,20 +1,27 @@
/**************************************************************************** /****************************************************************************
* * * *
* Project 64 - A Nintendo 64 emulator. * * Project 64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ * * http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. * * Copyright (C) 2012 Project64. All rights reserved. *
* * * *
* License: * * License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* * * *
****************************************************************************/ ****************************************************************************/
package emu.project64.game; package emu.project64.game;
import java.util.Timer;
import java.util.TimerTask;
import emu.project64.input.TouchController; import emu.project64.input.TouchController;
import emu.project64.input.map.VisibleTouchMap; import emu.project64.input.map.VisibleTouchMap;
import emu.project64.util.DeviceUtil; import emu.project64.util.DeviceUtil;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
@ -24,35 +31,84 @@ public class GameOverlay extends View implements TouchController.OnStateChangedL
private boolean mDrawingEnabled = true; private boolean mDrawingEnabled = true;
private int mHatRefreshPeriod = 0; private int mHatRefreshPeriod = 0;
private int mHatRefreshCount = 0; private int mHatRefreshCount = 0;
private Paint mPaint = new Paint();
public GameOverlay( Context context, AttributeSet attribs ) private Rect mRectangle = new Rect();
private String mDisplayMessage = "";
private String mDisplayMessage2 = "";
private int mDisplayMessageDuration = 0;
private Timer mTimer = null;
public GameOverlay(Context context, AttributeSet attribs)
{ {
super( context, attribs ); super(context, attribs);
requestFocus(); requestFocus();
} }
public void initialize( VisibleTouchMap touchMap, boolean drawingEnabled, boolean fpsEnabled, boolean joystickAnimated ) public void initialize( VisibleTouchMap touchMap, boolean drawingEnabled, boolean fpsEnabled, boolean joystickAnimated )
{ {
mTouchMap = touchMap; mTouchMap = touchMap;
mDrawingEnabled = drawingEnabled; mDrawingEnabled = drawingEnabled;
mHatRefreshPeriod = joystickAnimated ? 3 : 0; mHatRefreshPeriod = joystickAnimated ? 3 : 0;
} }
@Override public void SetDisplayMessage(String Message, int Duratation)
public void onAnalogChanged( float axisFractionX, float axisFractionY )
{ {
if( mHatRefreshPeriod > 0 && mDrawingEnabled ) if (Duratation >= mDisplayMessageDuration)
{
mDisplayMessage = Message;
mDisplayMessageDuration = Duratation;
if (mTimer != null)
{
mTimer.cancel();
mTimer.purge();
}
if (Duratation == 0)
{
Duratation = 10;
}
TimerTask task = new TimerTask()
{
@Override
public void run()
{
mDisplayMessage = "";
mDisplayMessageDuration = 0;
Timer CurrentTimer = mTimer;
mTimer = null;
CurrentTimer.cancel();
CurrentTimer.purge();
}
};
mTimer = new Timer();
mTimer.schedule(task, Duratation * 1000);
}
postInvalidate();
}
public void SetDisplayMessage2(String Message)
{
mDisplayMessage2 = Message;
postInvalidate();
}
@Override
public void onAnalogChanged(float axisFractionX, float axisFractionY)
{
if (mHatRefreshPeriod > 0 && mDrawingEnabled)
{ {
// Increment the count since last refresh // Increment the count since last refresh
mHatRefreshCount++; mHatRefreshCount++;
// If stick re-centered, always refresh // If stick re-centered, always refresh
if( axisFractionX == 0 && axisFractionY == 0 ) if (axisFractionX == 0 && axisFractionY == 0)
{
mHatRefreshCount = 0; mHatRefreshCount = 0;
}
// Update the analog stick assets and redraw if required // Update the analog stick assets and redraw if required
if( mHatRefreshCount % mHatRefreshPeriod == 0 && mTouchMap != null if (mHatRefreshCount % mHatRefreshPeriod == 0 && mTouchMap != null
&& mTouchMap.updateAnalog( axisFractionX, axisFractionY ) ) && mTouchMap.updateAnalog(axisFractionX, axisFractionY))
{ {
postInvalidate(); postInvalidate();
} }
@ -60,40 +116,64 @@ public class GameOverlay extends View implements TouchController.OnStateChangedL
} }
@Override @Override
public void onAutoHold( boolean autoHold, int index ) public void onAutoHold(boolean autoHold, int index)
{ {
// Update the AutoHold mask, and redraw if required // Update the AutoHold mask, and redraw if required
if( mTouchMap != null && mTouchMap.updateAutoHold( autoHold , index) ) if (mTouchMap != null && mTouchMap.updateAutoHold(autoHold, index))
{ {
postInvalidate(); postInvalidate();
} }
} }
@Override @Override
protected void onSizeChanged( int w, int h, int oldw, int oldh ) protected void onSizeChanged(int w, int h, int oldw, int oldh)
{ {
// Recompute skin layout geometry // Recompute skin layout geometry
if( mTouchMap != null ) if (mTouchMap != null)
mTouchMap.resize( w, h, DeviceUtil.getDisplayMetrics( this ) ); {
super.onSizeChanged( w, h, oldw, oldh ); mTouchMap.resize(w, h, DeviceUtil.getDisplayMetrics(this));
}
super.onSizeChanged(w, h, oldw, oldh);
} }
@Override @Override
protected void onDraw( Canvas canvas ) protected void onDraw(Canvas canvas)
{ {
if(canvas == null ) if (canvas == null)
{
return; return;
}
if( mTouchMap != null && mDrawingEnabled )
if (mTouchMap != null && mDrawingEnabled)
{ {
// Redraw the static buttons // Redraw the static buttons
mTouchMap.drawButtons( canvas ); mTouchMap.drawButtons(canvas);
// Redraw the dynamic analog stick // Redraw the dynamic analog stick
mTouchMap.drawAnalog( canvas ); mTouchMap.drawAnalog(canvas);
// Redraw the autoHold mask // Redraw the autoHold mask
mTouchMap.drawAutoHold( canvas ); mTouchMap.drawAutoHold(canvas);
}
String txt = mDisplayMessage;
if (txt.length() > 0 && mDisplayMessage2.length() > 0)
{
txt += " - ";
}
txt += mDisplayMessage2;
if (txt.length() > 0)
{
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.parseColor("#DDDDDD"));
mPaint.setAntiAlias(true);
mPaint.setTextSize(25);
mPaint.setTextAlign(Paint.Align.CENTER);
Typeface typeface = Typeface.create(Typeface.SANS_SERIF,
Typeface.BOLD_ITALIC);
mPaint.setTypeface(typeface);
mPaint.getTextBounds(txt, 0, txt.length(), mRectangle);
canvas.drawText(txt, Math.abs(canvas.getWidth() / 2), Math.abs((int) (canvas.getHeight() * 0.95)) - Math.abs(mRectangle.height()), mPaint);
} }
} }
} }

View File

@ -10,20 +10,18 @@
****************************************************************************/ ****************************************************************************/
package emu.project64.util; package emu.project64.util;
import emu.project64.game.GameOverlay;
import emu.project64.R;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.util.Log; import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
/** /**
* A small class to encapsulate the notification process for Mupen64PlusAE. * A small class to encapsulate the notification process for Mupen64PlusAE.
*/ */
public final class Notifier public final class Notifier
{ {
private static Toast sToast = null;
private static Runnable sToastMessager = null;
private static Runnable sDisplayMessager = null; private static Runnable sDisplayMessager = null;
/** /**
@ -80,50 +78,27 @@ public final class Notifier
Log.d("DisplayError", "Done"); Log.d("DisplayError", "Done");
} }
/** public static void showMessage( Activity activity, String message, int Duratation )
* Pop up a temporary message on the device.
*
* @param activity The activity to display from
* @param message The message string to display.
*/
public static void showToast( Activity activity, String message )
{ {
if( activity == null ) if( activity == null )
return; return;
// Create a messaging task if it doesn't already exist
if( sToastMessager == null )
{
final String ToastMessage = new String(message);
final Activity ToastActivity = activity;
sToastMessager = new Runnable() GameOverlay overlay = (GameOverlay) activity.findViewById(R.id.gameOverlay);
{ if (overlay == null)
@Override return;
public void run()
{ overlay.SetDisplayMessage(message, Duratation);
// Just show the toast message
if( sToast != null )
sToast.show();
if( sToast != null )
{
// Toast exists, just change the text
Notifier.sToast.setText( ToastMessage );
}
else
{
// Message short in duration, and at the bottom of the screen
sToast = Toast.makeText( ToastActivity, ToastMessage, Toast.LENGTH_SHORT );
sToast.setGravity( Gravity.BOTTOM, 0, 0 );
}
sToastMessager = null;
}
};
}
activity.runOnUiThread( sToastMessager );
} }
public static void showMessage2( Activity activity, String message )
{
if( activity == null )
return;
GameOverlay overlay = (GameOverlay) activity.findViewById(R.id.gameOverlay);
overlay.SetDisplayMessage2(message);
}
private static Runnable runEmulationStopped = null; private static Runnable runEmulationStopped = null;
public static void EmulationStopped (Activity activity) public static void EmulationStopped (Activity activity)
{ {

View File

@ -122,14 +122,26 @@ void JavaBridge::DisplayError(const char * Message)
} }
} }
void JavaBridge::DisplayMessage(const char * Message) void JavaBridge::DisplayMessage(const char * Message, int DisplayTime)
{ {
JNIEnv *env = Android_JNI_GetEnv(); JNIEnv *env = Android_JNI_GetEnv();
if (env) if (env)
{ {
jstring j_Message = env->NewStringUTF(Message); jstring j_Message = env->NewStringUTF(Message);
jmethodID midShowToast = env->GetStaticMethodID(m_NotifierClass, "showToast", "(Landroid/app/Activity;Ljava/lang/String;)V"); jmethodID midshowMessage = env->GetStaticMethodID(m_NotifierClass, "showMessage", "(Landroid/app/Activity;Ljava/lang/String;I)V");
env->CallStaticVoidMethod(m_NotifierClass, midShowToast,g_Activity,j_Message); env->CallStaticVoidMethod(m_NotifierClass, midshowMessage,g_Activity,j_Message,DisplayTime);
env->DeleteLocalRef(j_Message);
}
}
void JavaBridge::DisplayMessage2(const char * Message)
{
JNIEnv *env = Android_JNI_GetEnv();
if (env)
{
jstring j_Message = env->NewStringUTF(Message);
jmethodID midshowMessage2 = env->GetStaticMethodID(m_NotifierClass, "showMessage2", "(Landroid/app/Activity;Ljava/lang/String;)V");
env->CallStaticVoidMethod(m_NotifierClass, midshowMessage2,g_Activity,j_Message);
env->DeleteLocalRef(j_Message); env->DeleteLocalRef(j_Message);
} }
} }

View File

@ -23,7 +23,8 @@ public:
//Notification //Notification
void DisplayError(const char * Message); void DisplayError(const char * Message);
void DisplayMessage(const char * Message); void DisplayMessage(const char * Message, int DisplayTime);
void DisplayMessage2(const char * Message);
void EmulationStopped(void); void EmulationStopped(void);
private: private:

View File

@ -28,7 +28,7 @@ CNotificationImp & Notify(void)
} }
CNotificationImp::CNotificationImp() : CNotificationImp::CNotificationImp() :
m_NextMsg(0) m_NextMsg(0)
{ {
} }
@ -81,26 +81,7 @@ void CNotificationImp::DisplayMessage(int DisplayTime, const char * Message) con
{ {
#ifdef ANDROID #ifdef ANDROID
if (g_JavaBridge == NULL) { return; } if (g_JavaBridge == NULL) { return; }
g_JavaBridge->DisplayMessage(Message, DisplayTime);
if (m_NextMsg > 0 || DisplayTime > 0)
{
time_t Now = time(NULL);
if (DisplayTime == 0 && Now < m_NextMsg)
{
return;
}
if (DisplayTime > 0)
{
m_NextMsg = Now + DisplayTime;
}
if (m_NextMsg == 0)
{
m_NextMsg = 0;
}
}
m_Message[0] = Message;
UpdateMessage();
#else #else
// ignore warning usage // ignore warning usage
DisplayTime = DisplayTime; DisplayTime = DisplayTime;
@ -112,9 +93,8 @@ void CNotificationImp::DisplayMessage2(const char * Message) const
{ {
#ifdef ANDROID #ifdef ANDROID
if (g_JavaBridge == NULL) { return; } if (g_JavaBridge == NULL) { return; }
m_Message[1] = Message;
UpdateMessage(); g_JavaBridge->DisplayMessage2(Message);
#else #else
// ignore warning usage // ignore warning usage
Message = Message; Message = Message;
@ -151,19 +131,3 @@ bool CNotificationImp::ProcessGuiMessages(void) const
void CNotificationImp::ChangeFullScreen(void) const void CNotificationImp::ChangeFullScreen(void) const
{ {
} }
void CNotificationImp::UpdateMessage(void) const
{
#ifdef ANDROID
std::string message = m_Message[0];
if (message.length() > 0 && m_Message[1].length())
{
message += " ";
}
message += m_Message[1];
if (message.length() > 0)
{
g_JavaBridge->DisplayMessage(message.c_str());
}
#endif
}

View File

@ -17,7 +17,7 @@ class CNotificationImp :
{ {
public: public:
CNotificationImp(void); CNotificationImp(void);
virtual ~CNotificationImp(); virtual ~CNotificationImp();
//Error Messages //Error Messages
void DisplayError(const char * Message) const; void DisplayError(const char * Message) const;
@ -44,10 +44,7 @@ private:
CNotificationImp(const CNotificationImp&); // Disable copy constructor CNotificationImp(const CNotificationImp&); // Disable copy constructor
CNotificationImp& operator=(const CNotificationImp&); // Disable assignment CNotificationImp& operator=(const CNotificationImp&); // Disable assignment
void UpdateMessage(void) const;
mutable time_t m_NextMsg; mutable time_t m_NextMsg;
mutable std::string m_Message[2];
}; };
CNotificationImp & Notify(void); CNotificationImp & Notify(void);