I created a splash screen in Android Studio. Now I want a text to speech function to say:
Done by Me
This should happen when the splash screen opens. How do I go about this?
Here is my program so far:
public class CinemaList extends Activity {
private static int SPLASH_TIME_OUT = 4000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent homeIntent = new Intent(CinemaList.this, MovieList.class);
startActivity(homeIntent);
finish();
}
}, SPLASH_TIME_OUT);
}
}
I hope that you're enjoying learning Android development. It can be done as follows:
public class SplashActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
private TextToSpeech mTts;
private static final String TAG = MainActivity.class.getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTts = new TextToSpeech(this, this);
}
#Override
protected void onPause() {
if(mTts != null){
mTts.stop();
mTts.shutdown();
}
super.onPause();
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onDone(String utteranceId) {
Log.d(TAG, "Done: " + utteranceId);
Intent homeIntent = new Intent(CinemaList.this, MovieList.class);
startActivity(homeIntent);
}
#Override
public void onError(String utteranceId) {
Log.e(TAG, "Error: " + utteranceId);
}
#Override
public void onStart(String utteranceId) {
Log.i(TAG, "Started: " + utteranceId);
}
});
mTts.speak("Done by ME!", TextToSpeech.QUEUE_ADD, null);
} else {
Log.e(TAG, "Failed");
}
}
}
I took the liberty of starting the 'Cinema List' activity after the speech completes. However, you can continue to use your Handler if you wish.
Also, rather than calling finish() explicitly, I'd advise adding the noHistory flag to the SplashActivity in the manifest:
<activity
android:name=".SplashActivity"
android:noHistory="true"/>
The noHistory flag ensures that the Activity will not be there when back is pressed.
noHistory vs finish() - Which is preferred?
Related
I have written this class:
public class SplashScreen extends AppCompatActivity {
private Handler the_transition_handler;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
}
#Override
protected void onStart() {
super.onStart();
startTheTransitionAfterTheSplashScreen();
}
#Override
protected void onDestroy() {
super.onDestroy();
the_transition_handler.removeCallbacksAndMessages(null);
}
private void startTheTransitionAfterTheSplashScreen() {
the_transition_handler = new Handler();
the_transition_handler.postDelayed(new Runnable() {
#Override
public void run() {
final Intent intentSplashScreenToActivityJustAfterSplashScreen = new Intent(SplashScreen.this, ActivityJustAfterSplashScreen.class);
startActivity(intentSplashScreenToActivityJustAfterSplashScreen);
overridePendingTransition(R.anim.animation_enter_activity, R.anim.animation_leave_activity);
finish();
}
}, 1000);
}
}
My question is: since the run callback is executed after the time I've indicated (according to this doc: https://developer.android.com/reference/android/os/Handler) , should I replace its content with the following code (assuming that being an AppCompatActivity)?
#Override
public void run() {
if(that == null || that.isDestroyed()) {
return;
}
final Intent intentSplashScreenToActivityJustAfterSplashScreen = new Intent(SplashScreen.this, ActivityJustAfterSplashScreen.class);
startActivity(intentSplashScreenToActivityJustAfterSplashScreen);
overridePendingTransition(R.anim.animation_enter_activity, R.anim.animation_leave_activity);
finish();
}
Note that Android Studio says that == null is always false and should be removed.
Use isDestroyed() || isFinishing() or just call removeCallbacksAndMessages to remove any pending posts of callbacks:
#Override
protected void onDestroy() {
if (the_transition_handler != null) {
the_transition_handler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
This is a test activity when the button is pressed the textToSpeech works just fine, but it wont work when the function playString() is called, playString() is being called from the onCreate() of this TestActivity.
public class TestActivity extends Activity {
TextToSpeech textToSpeech;
EditText editText;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
editText=(EditText)findViewById(R.id.editText);
button=(Button)findViewById(R.id.button);
textToSpeech=new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if(status != TextToSpeech.ERROR) {
textToSpeech.setLanguage(Locale.UK);
}
}
});
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
});
playString();
}
public void playString(){
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
public void onPause(){
if(textToSpeech !=null){
textToSpeech.stop();
textToSpeech.shutdown();
}
super.onPause();
}
}
From documentation:
TextToSpeech instance can only be used to synthesize text once it has completed its initialization.
Initialization may take long time (on my device it's take ~30 seconds), so you can't use handler with some random delay.
Instead, you can place playString() in onInit block right after textToSpeech.setLanguage(Locale.UK);, so string will be played when it can be played.
Please use below code in oncreate method to call the texttospeech:
textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
if (status != TextToSpeech.ERROR) {
textToSpeech.setLanguage(Locale.UK);
}
}
});
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
String sentence = "Testing String";
textToSpeech.speak(sentence, TextToSpeech.QUEUE_FLUSH, null);
}
}, 500);
I run the code and get the following result, but I hope that the App can run at the order "A" -> "Service OnDestroy" -> "B" -> "C", how can I do ?
In My Way 2 section, I try to place the code into the function new Handler().postDelayed(new Runnable() {}, it's OK , it ran at the order "A" -> "Service OnDestroy" ->"B" ->"C",
I don't konw why the way can success, I don't know if the way is good way!
Result
11-13 10:04:32.137 27947-27947/info.dodata.screenrecorder E/My﹕ A
11-13 10:04:32.147 27947-27947/info.dodata.screenrecorder E/My﹕ B
11-13 10:04:32.157 27947-27947/info.dodata.screenrecorder E/My﹕ C
11-13 10:04:32.157 27947-27947/info.dodata.screenrecorder E/My﹕ Service OnDestroy
UIAbou.cs
public class UIAbout extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_about);
Intent intent1 = new Intent(UIAbout.this,bll.RecordService.class);
startService(intent1);
Button btnReturn = (Button) findViewById(R.id.btnReturn);
btnReturn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.e("My", "A");
Intent intent1 = new Intent(UIAbout.this,bll.RecordService.class);
stopService(intent1);
Log.e("My", "B");
Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_LONG).show();
Log.e("My", "C");
}
});
}
}
RecordService.cs
public class RecordService extends Service {
private Context mContext;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
}
#Override
public void onDestroy(){
Log.e("My","Service OnDestroy");
super.onDestroy(); //It seems that the APP is OK if I remove this.
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent,flags,startId);
}
}
=======================My Way 1 ======================================
I set a mark isServiceStoped to monitor if Stop Service is finished, but my app is hang up after disply the result "11-13 11:31:23.107 7599-7599/info.dodata.screenrecorder E/My﹕ A"
New UIAbout.cs
public class UIAbout extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_about);
Intent intent1 = new Intent(UIAbout.this,bll.RecordService.class);
startService(intent1);
Button btnReturn = (Button) findViewById(R.id.btnReturn);
btnReturn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.e("My", "A");
Intent intent1 = new Intent(UIAbout.this, bll.RecordService.class);
stopService(intent1);
while (RecordService.isServiceStoped==false){
//It block
}
Log.e("My", "B");
Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_LONG).show();
Log.e("My", "C");
}
});
}
}
New RecordService.cs
public class RecordService extends Service {
public static boolean isServiceStoped=true;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
}
#Override
public void onDestroy(){
Log.e("My", "Service OnDestroy");
isServiceStoped=true;
super.onDestroy(); //It seems that the APP is OK if I remove this.
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
isServiceStoped=false;
return super.onStartCommand(intent,flags,startId);
}
}
=====================My Way 2==========================================
I try to place the code into the function new Handler().postDelayed(new Runnable() {}, it's OK , it ran at the order "A" -> "Service OnDestroy" ->"B" ->"C",
I don't konw why the way can success, I don't know if the way is good way
The last UIAbout.cs
public class UIAbout extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_about);
Intent intent1 = new Intent(UIAbout.this,bll.RecordService.class);
startService(intent1);
Button btnReturn = (Button) findViewById(R.id.btnReturn);
btnReturn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.e("My", "A");
Intent intent1 = new Intent(UIAbout.this, bll.RecordService.class);
stopService(intent1);
new Handler().postDelayed(new Runnable() {
public void run() {
Log.e("My", "B");
Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_LONG).show();
Log.e("My", "C");
}
}, 1);
}
});
}
}
The last RecordService.cs
public class RecordService extends Service {
private Context mContext;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
}
#Override
public void onDestroy(){
Log.e("My", "Service OnDestroy");
super.onDestroy(); //It seems that the APP is OK if I remove this.
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent,flags,startId);
}
}
No, you can't stop a service synchronously. stopService() is request to stop the service. It will stop sometime later, as soon as it can.
No, you can't remove super.onDestroy() from your onDestroy() method and still have it work properly.
You can not control the timing to completely stop the running service. Use stopService() and the rest is out of your hands. You can use an handler to monitor is the service has stopped before moving to B although I am not sure why would you do it. Not a good practice.
Yeah you can remove super.onDestroy() in onDestroy but I would not advise you to do so. Your app may run but it will be leaving unwanted resources around.
Here how onDestroy() looks like in the android SDK:
#CallSuper
protected void onDestroy() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
mCalled = true;
// dismiss any dialogs we are managing.
if (mManagedDialogs != null) {
final int numDialogs = mManagedDialogs.size();
for (int i = 0; i < numDialogs; i++) {
final ManagedDialog md = mManagedDialogs.valueAt(i);
if (md.mDialog.isShowing()) {
md.mDialog.dismiss();
}
}
mManagedDialogs = null;
}
// close any cursors we are managing.
synchronized (mManagedCursors) {
int numCursors = mManagedCursors.size();
for (int i = 0; i < numCursors; i++) {
ManagedCursor c = mManagedCursors.get(i);
if (c != null) {
c.mCursor.close();
}
}
mManagedCursors.clear();
}
// Close any open search dialog
if (mSearchManager != null) {
mSearchManager.stopSearch();
}
getApplication().dispatchActivityDestroyed(this);
}
* Sample *
There could be some compile errors, but you will get the idea.
public class UIAbout extends Activity {
private Handler mHandler = new Handler();
private Runnable checkServiceHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_about);
Intent intent1 = new Intent(UIAbout.this,bll.RecordService.class);
startService(intent1);
Button btnReturn = (Button) findViewById(R.id.btnReturn);
btnReturn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.e("My", "A");
Intent intent1 = new Intent(UIAbout.this, bll.RecordService.class);
stopService(intent1);
checkServiceHandler = new Runnable() {
public void run() {
if(RecordService.isServiceStoped){
mHandler.removeCallbacks(checkServiceHandler );
somemethod();
} else{
mHandler.postDelayed(checkServiceHandler, 500);
}
}
};
mHandler.postDelayed(checkServiceHandler, 500); }
});
}
private void somemethod(){
Log.e("My", "B");
Toast.makeText(getApplicationContext(), "OK", Toast.LENGTH_LONG).show();
Log.e("My", "C");
}
}
You should implement your code in a way that you don't care when exactly your service is destroyed.
Anyway. If you really need the exact moment, you can fire an intent from your service using Android's broadcast system.
In your service:
#Override
public void onDestroy()
{
super.onDestroy();
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(CONST_SERVICE_DESTROYED));
}
In your activity:
private BroadcastReceiver receiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
// your B here
// your C here
}
};
And you need to register and unregister your receiver like this:
#Override
protected void onResume()
{
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, new IntentFilter(CONST_SERVICE_DESTROYED));
}
#Override
protected void onPause()
{
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
A good explanation and examples of Android's broadcast system can be found here
I need just a few minutes for someone to tell me if these steps are correct for implementing cordova in a android webview:
EDIT: Ok I finally got it working these are the right steps:
1) I create project: cordova create hello com.example.hello HelloWorld and enter the folder
2) cordova platform add android, cordova run android (cordova.jar is created) => the app is launched => device is ready is shown
3) I create a cordova_layout.xml in "/res/layout" with this code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<org.apache.cordova.CordovaWebView
android:id="#+id/cordova_web_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
</LinearLayout>
4)Import the project (as an "existing project" in eclipse) and add to the main java file after imports:
public class HelloWorld extends Activity implements CordovaInterface {
private CordovaWebView cordova_webview;
private String TAG = "CORDOVA_ACTIVITY";
private final ExecutorService threadPool = Executors.newCachedThreadPool();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cordova_layout);
cordova_webview = (CordovaWebView) findViewById(R.id.cordova_web_view);
// Config.init(this);
String url = "file:///android_asset/www/index.html";
cordova_webview.loadUrl(url, 10000);
}
#Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
protected void onDestroy() {
super.onDestroy();
if (this.cordova_webview != null) {
this.cordova_webview
.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
this.cordova_webview.loadUrl("about:blank");
cordova_webview.handleDestroy();
}
}
#Override
public Activity getActivity() {
return this;
}
#Override
public ExecutorService getThreadPool() {
return threadPool;
}
#Override
public Object onMessage(String message, Object obj) {
Log.d(TAG, message);
if (message.equalsIgnoreCase("exit")) {
super.finish();
}
return null;
}
#Override
public void setActivityResultCallback(CordovaPlugin cordovaPlugin) {
Log.d(TAG, "setActivityResultCallback is unimplemented");
}
#Override
public void startActivityForResult(CordovaPlugin cordovaPlugin,
Intent intent, int resultCode) {
Log.d(TAG, "startActivityForResult is unimplemented");
}
}
NOTE: the activity name must match the one in manifest.xml
Hope it will help you.
Have a nice day!
If you want to load an url in a phonegap app then you may use the below code to load your first url from asset
public class MyPhoneGapActivity extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.loadUrl("file:///android_asset/www/index.html", 10000);
}
For embedding a cordova webview in native android application and loading an url use the below code
public class CordovaActivity extends Activity implements CordovaInterface {
private CordovaWebView cordova_webview;
private String TAG = "CORDOVA_ACTIVITY";
private final ExecutorService threadPool = Executors.newCachedThreadPool();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cordova_layout);
cordova_webview = (CordovaWebView) findViewById(R.id.cordova_web_view);
// Config.init(this);
String url = "file:///android_asset/www/index.html";
cordova_webview.loadUrl(url, 10000);
}
#Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
protected void onDestroy() {
super.onDestroy();
if (this.cordova_webview != null) {
this.cordova_webview
.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
this.cordova_webview.loadUrl("about:blank");
cordova_webview.handleDestroy();
}
}
#Override
public Activity getActivity() {
return this;
}
#Override
public ExecutorService getThreadPool() {
return threadPool;
}
#Override
public Object onMessage(String message, Object obj) {
Log.d(TAG, message);
if (message.equalsIgnoreCase("exit")) {
super.finish();
}
return null;
}
#Override
public void setActivityResultCallback(CordovaPlugin cordovaPlugin) {
Log.d(TAG, "setActivityResultCallback is unimplemented");
}
#Override
public void startActivityForResult(CordovaPlugin cordovaPlugin,
Intent intent, int resultCode) {
Log.d(TAG, "startActivityForResult is unimplemented");
}
}
I'm really struggling with something... I have a couple of sentences that I want to read, both verbally through tts speek function, and via text on screen, one sentence at a time.
I have the textview area ready, but putting it together is what I'm not getting. Either it will read all the sentences and only show the last one, or it will show and read only the first sentence.
Anyone know i how I can accomplish this goal?
I just ran into this issue, according to the speak method, use an UtteranceProgressListener. I found out this is not executed on the UI thread, so I had to use runOnUiThread() to get back to update the activity.
tts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
}
#Override
public void onDone(String utteranceId) {
LettersActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Do something on UI thread
}
});
}
#Override
public void onError(String utteranceId) {
Log.e(TAG, "error on " + utteranceId);
}
});
boolean speakingEnd = tts.isSpeaking();
do{
speakingEnd = tts.isSpeaking();
} while (speakingEnd);
//Continue with code
public void speak(String message){
tts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
while (tts.isSpeaking()){
System.Out.Println("Do something or nothing while speaking..")
}
}
Try this
public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener{
private boolean initialized;
private String queuedText;
private String TAG = "TTS";
private TextToSpeech tts;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this /* context */, this /* listener */);
tts.setOnUtteranceProgressListener(mProgressListener);
speak("hello world");
}
public void speak(String text) {
if (!initialized) {
queuedText = text;
return;
}
queuedText = null;
setTtsListener(); // no longer creates a new UtteranceProgressListener each time
HashMap<String, String> map = new HashMap<String, String>();
map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "MessageId");
tts.speak(text, TextToSpeech.QUEUE_ADD, map);
}
private void setTtsListener() {
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
initialized = true;
tts.setLanguage(Locale.ENGLISH);
if (queuedText != null) {
speak(queuedText);
}
}
}
private abstract class runnable implements Runnable {
}
private UtteranceProgressListener mProgressListener = new UtteranceProgressListener() {
#Override
public void onStart(String utteranceId) {
} // Do nothing
#Override
public void onError(String utteranceId) {
} // Do nothing.
#Override
public void onDone(String utteranceId) {
new Thread()
{
public void run()
{
MainActivity.this.runOnUiThread(new runnable()
{
public void run()
{
Toast.makeText(getBaseContext(), "TTS Completed", Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
};
}