Showing posts with label android-service. Show all posts
Showing posts with label android-service. Show all posts

Wednesday, May 9, 2018

When is a started and bound Service destroyed?

Leave a Comment

I was going through the services documentation in android when I noticed two contradicting points:

In the services document it is specified in Managing the Lifecycle of a Service

These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.

But in the document about bound services in Managing the Lifecycle of a Bound Service

However, if you choose to implement the onStartCommand() callback method, then you must explicitly stop the service, because the service is now considered to be started. In this case, the service runs until the service stops itself with stopSelf() or another component calls stopService(), regardless of whether it is bound to any clients.

It may be me but I think the statements are contradictory.Could anyone please clarify...

3 Answers

Answers 1

Actually, both paragraphs complement each other (although their wording might be misguiding), and both paragraphs are consistent with the image from the documentation. Let's have a look:

These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.

The quintessence is: If you start a service, then bind a client to it, then try to stop it, the service is not stopped (destroyed) before all clients unbind. The second paragraph does not contradict, it refines this statement.

However, if you choose to implement the onStartCommand() callback method, then you must explicitly stop the service, because the service is now considered to be started. In this case, the service runs until the service stops itself with stopSelf() or another component calls stopService(), regardless of whether it is bound to any clients.

This means: A started and bound service runs even if no clients are bound to it until it is explicitely stopped. Granted, the wording might probably be a bit clearer on this. The lifecycle diagram given in the documentation however shows this (and I am pretty sure I already observed this in "real-life", although I am currently have no direct example on top of my head):

Lifecycle for started and bound services

Answers 2

Agree that the documentation could be clearer. What they are trying to say is:

  • If you call startService(), then the service will keep running unless and until you call stopSerivce() (or stopSelf() from within the service)
  • If you call bindService(), then the service will keep running unless and until you call unbindService()
  • Therefore, if you call both startService() and bindService(), then the service will keep running until you call both stopService and unbindService(). Neither on its own will stop the service.

Created a very simple Activity and Service and ran the following sequences of start/stop/bind/unbind. I observed that the calls gave the following results.

bind-unbind

bindService() caused:     onCreate()     onBind() unbindService() caused:     onUnbind()     onDestroy() 

start-bind-unbind-stop

startService() caused:     onCreate()     onStartCommand() bindService() caused:     onBind() unbindService() caused:     onUnbind() stopService() caused:     onDestroy() 

start-bind-stop-unbind

startService() caused:     onCreate()     onStartCommand() bindService() caused:     onBind() stopService() caused:     -- nothing unbindService() caused:     onUnbind()     onDestroy() 

bind-start-stop-unbind

bindService() caused:     onCreate()     onBind() startService() caused:     onStartCommand() stopService() caused:     -- nothing -- still running unbindService() caused:     onUnbind()     onDestroy() 

bind-start-unbind-stop

bindService() caused:     onCreate()     onBind() startService() caused:     onStartCommand() unbindService() caused:     onUnbind() stopService() caused:     onDestroy() 

As you can see, in each case where both bind and start were called, the service kept running until both unbind and stop were called. The sequence of unbind/stop is not important.

Here is the example code that was called from separate buttons in my simple test app:

public void onBindBtnClick(View view) {     Intent intent = new Intent(MainActivity.this, ExampleService.class);     bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); }  public void onUnbindBtnClick(View view) {     if (serviceIsBound) {         unbindService(serviceConnection);         serviceIsBound = false;     } }  public void onStartBtnClick(View view) {     Intent intent = new Intent(MainActivity.this, ExampleService.class);     startService(intent); }  public void onStopBtnClick(View view) {     Intent intent = new Intent(MainActivity.this, ExampleService.class);     exampleService.stopService(intent); } 

Answers 3

Yep, it works. I want to complete with a sample code :

I had to make an app with a service started by an activity, the activity have to call some methods in the service, the service have to run in background even if the activity were killed, and when the activity restarts, it haven't to restart the service if it is running. I hope it will help you, you can see how does it work with the Log. So that is the code :

 public class MyActivity extends Activity{      private MyService myService;     private boolean mIsBound = false;      private ServiceConnection mConnection = new ServiceConnection() {          public void onServiceConnected(ComponentName className, IBinder binder) {             MyService.MyBinder b = (MyService.MyBinder) binder;             myService = b.getService();             mIsBound = true             //Do something             // Here you can call : myService.aFonctionInMyService();          }         public void onServiceDisconnected(ComponentName className) {             // Do something             mIsBound = false;         }     }        protected void onCreate(Bundle savedInstanceState) {         // TODO Auto-generated method stub         super.onCreate(savedInstanceState);          //Checked if my service is running         if (!isMyServiceRunning()) {             //if not, I start it.             startService(new Intent(this,MyService.class));         }     }      private boolean isMyServiceRunning() {         ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);         for (RunningServiceInfo service : manager                 .getRunningServices(Integer.MAX_VALUE)) {             if (MyService.class.getName().equals(                     service.service.getClassName())) {                 return true;             }         }         return false;     }      @Override     protected void onResume() {         // TODO Auto-generated method stub         super.onResume();         doBindService();     }         //Connection to the Service     private void doBindService() {         bindService(new Intent(this,MyService.class), mConnection,                 Context.BIND_AUTO_CREATE);     }      // Disconnection from the service     private void doUnbindService() {         if (mIsBound) {             // Detach our existing connection.             unbindService(mConnection);         }     }      @Override     protected void onPause() {         // TODO Auto-generated method stub         doUnbindService();         super.onPause();     }  }   public class MyService extends Service{       public static String Tag = "MyService";     private final IBinder mBinder = new MyBinder();      @Override     public void onCreate() {         // TODO Auto-generated method stub               super.onCreate();         Log.d(Tag, "onCreate()");      }      public class MyBinder extends Binder {         public LocationService getService() {             return LocationService.this;         }     }      @Override     public IBinder onBind(Intent intent) {         // TODO Auto-generated method stub         Log.d(Tag, "onBind()");         return mBinder;     }      @Override     public boolean onUnbind(Intent intent) {         // TODO Auto-generated method stub         Log.d(Tag, "onUnBind()");         return super.onUnbind(intent);     }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         // TODO Auto-generated method stub         Log.d(Tag,"onStartCommand()");          return START_STICKY;     }      @Override     public void onDestroy() {         // TODO Auto-generated method stub          Log.d(Tag, "onDestroy");         super.onDestroy();     }      public void aFonctionInMyService(){         //Do Something     }  } 
Read More

Thursday, April 19, 2018

Hide notification from Foreground Service on click notification Action

Leave a Comment

I have an Alarm App that have foreground service with a Heads-Up Notification and that notification have two actions where one send an intent to the Service and can open an activity depending on the app configuration.

The problem is that when i click on a action that sends the intent to the service the notification doesn't hide. This not seems to occur when the intent opens a Activity

I don't want a foreground service without a Notification, i just want it to hide it back to the Notification Drawer when the intent is sent to the service

Here is the code:

NotificationCompat.Builder(mAlarmApplication, CHANNEL_ID)             .setSmallIcon(R.drawable.ic_notification_alarm)             .setAutoCancel(false)             .setOngoing(true)             .setVibrate(LongArray(0))             .setContentTitle("Title")             .setContentText("Content")             .addAction(0, dismissActionText, dismissPendingIntent)             .setCategory(NotificationCompat.CATEGORY_ALARM)             .setPriority(NotificationCompat.PRIORITY_MAX)             .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)             .setContentIntent(alarmScreenPendingIntent)             .setFullScreenIntent(alarmScreenPendingIntent, true) 

Here is the link of the app https://play.google.com/store/apps/details?id=com.garageapp.alarmchallenges.

The problem occurs when alarm start and my current solution is to update the old heads up notification with a new one that is not a heads up but the UX is not a good because on Android 8+ the notification new notification pops up aging

4 Answers

Answers 1

Seems like your Notification is bonded with your Service. If so, then you have to kill the notification in Service

Did you try?

public static void cancelNotification(Context ctx, int notifyId) {     String ns = Context.NOTIFICATION_SERVICE;     NotificationManager nMgr = (NotificationManager) ctx.getSystemService(ns);     nMgr.cancel(notifyId); } 

Answers 2

You are using .setOngoing(true) which should not be removed while service is working.

.setAutoCancel(true) will also not working with .setOngoing(true).

You have to use .setOngoing(false) to dismiss the notification.

Answers 3

If you or user remove your foreground notification your service will go to background, I think that best work is to not using heads up notification for foreground by not setting its priority to MAX

Use two notifications at same time one in drawer and another heads up:

-The first notification with priority DEFAULT for starting foreground ( auto cancel set to false and ongoing set to true) show this one with startForground()

-The Second notification (Heads up (Priority MAX) auto cancel set to true and on going set to false) for your actions show this with notifyManager.notify()

These two notifications must have different IDs


another solution:

If you want to use one heads up notification with actions for foreground service you may do this:

use a heads up notification with your action buttons for foreground service when the user clicks actions this action must call the foreground service and then the foreground service could call startForeground (with same id) with a new notification with priority set to default, if your notification could not be updated you may need to call stopForeground(true) or notificationManager.cancel(id) first before calling startForeground with new notification. both of these two notifications should has on going set to true and auto cancel set to false

In my opinion the first solution is better than the second because the notification may not update in second solution.

Answers 4

As the documentation says :

A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of ...

android system does not allow you to have a foreground service without notification or a hidden notification. and that's because of user awareness of what is happening in his/her system.

also killing the notification will stop your foreground service. so you never can have both of the options (foreground service and hidden notification)

a not clear solution for your problem:

when you call action that sends the intent to the service, do this with a mediator activity i mean first open an activity and in the activity send intent to the service.

I hope this solve your problem as you told :

The problem is that when i click on a action that sends the intent to the service the notification doesn't hide. This not seems to occur when the intent opens a Activity

Read More

Sunday, February 18, 2018

Android process is bad error on killing the app

Leave a Comment

Here is the exact error

02-08 12:36:43.490 3479-4980/? W/ActivityManager: Scheduling restart of crashed service com.wfl/.StepTrackerShakeDetectorService in 1000ms  02-08 12:36:44.494 3479-3513/? W/ActivityManager: Unable to launch app com.wfl/10139 for service Intent { cmp=com.wfl/.StepTrackerShakeDetectorService }: process is bad 

Here is the scenario It is basically a step tracker

StepTrackerShakeDetectorService is implemented to restart automatically when app is destroyed using START_STICKY

But when the app is removed from task list I am getting this error.

Here is the code.

public class StepTrackerShakeDetectorService extends Service {      private SensorManager mSensorManager;     private StepTrackerShakeDetector mShakeDetector;     private Sensor step_counter_sensor;     private Sensor step_detector_sensor;     private Sensor step_accelerometer;      @Override     public IBinder onBind(Intent intent) {         return null;     }      @Override     public void onCreate() {          registerDetector();     }       private void registerDetector() {          mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);          step_counter_sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);         step_detector_sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);         step_accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);          if (step_counter_sensor != null) // sensor not supported         {             mShakeDetector = new StepTrackerShakeDetector(Sensor.TYPE_STEP_COUNTER);             mSensorManager.registerListener(mShakeDetector, step_counter_sensor, SensorManager.SENSOR_DELAY_FASTEST);         } else if (step_accelerometer != null) {              mShakeDetector = new StepTrackerShakeDetector(Sensor.TYPE_ACCELEROMETER);             mSensorManager.registerListener(mShakeDetector, step_accelerometer, SensorManager.SENSOR_DELAY_FASTEST);         }           mShakeDetector.setOnShakeListener(new StepTrackerShakeDetector.OnShakeListener() {              @Override             public void onShake(int count) {               //Code to calculate steps             }         });     }      private void unregisterDetector() {         mSensorManager.unregisterListener(mShakeDetector);     }      @Override     public void onStart(Intent intent, int startId) {         super.onStart(intent, startId);     }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         return START_STICKY;     }      @Override     public void onDestroy() {         unregisterDetector();         super.onDestroy();     }      @Override     public void onTaskRemoved(Intent rootIntent) {         super.onTaskRemoved(rootIntent);         Intent intent = new Intent(getApplicationContext(), StepTrackerShakeDetectorService.class);         PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_ONE_SHOT);         AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);         alarmManager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 2000, pendingIntent);     }   } 

Here is the error in lenovo phab 2

02-13 11:42:12.211 975-1964/? W/ActivityManager: Scheduling restart of crashed service com.wfl/.StepTrackerShakeDetectorService in 1000ms 02-13 11:42:12.213 975-1964/? I/ActivityManager:   Force stopping service ServiceRecord{7a45ff2 u0 com.wfl/.StepTrackerShakeDetectorService} 02-13 11:42:12.214 975-1964/? V/ActivityManager: Broadcast: Intent { act=android.intent.action.PACKAGE_RESTARTED dat=package:com.wfl flg=0x10 (has extras) } ordered=false userid=0 callerApp=null 02-13 11:42:12.216 975-2003/? W/ActivityManager: Spurious death for ProcessRecord{2325a63 0:com.wfl/u0a146}, curProc for 30626: null 02-13 11:42:12.790 2342-2361/? D/GasService: FG app changed: from com.wfl to  

2 Answers

Answers 1

Put this line at the end of the function in onTaskRemoved

        super.onTaskRemoved(rootIntent); 

Answers 2

Change your onbind method which returns null to

@Override public IBinder onBind(Intent intent) {     return new Binder(); } 

and run again if not working please see the below links

Why does my Android service get restarted when the process is killed, even though I used START_NOT_STICKY?

In the below link, one is using the same procedure to get alarm services and the others services in back ground check this out also

Service crashing and restarting

Read More

Sunday, February 4, 2018

Can a third-party app implement CallScreeningService in android 7?

Leave a Comment

Android API level 24 introduces a new Service called the CallScreeningService. The documentation says that the service can by implemented by the default dialer to screen incoming calls. I would like to implement this service in my own app, preferably without creating an entire dialer app, but a simple naive implementation seems to be ignored by the OS when an incoming call happens.

AndroidManifest.xml snippet:

<service android:name="com.example.callbouncer.CallService" android:permission="android.permission.BIND_SCREENING_SERVICE">     <intent-filter>         <action android:name="android.telecom.CallScreeningService"/>     </intent-filter> </service> 

CallService.java:

// imports... public class CallService extends CallScreeningService {     @Override     public void onScreenCall(Call.Details callDetails) {         CallResponse.Builder response = new CallResponse.Builder();         Log.e("CallBouncer", "Call screening service triggered");         respondToCall(callDetails, response.build() );     } } 

There are no errors while building or installing this program, but the screening doesn't seem to be taking place. Have I done something wrong (like the manifest or missing implementations/overrides in the service) or is it just not possible? If it's not possible in a small app like this, will it be possible if I implement an entire dialing app and set it as the default dialer? Finally, if that's the case, is there anything preventing me from just forking the dialer out of the AOSP and adding my features to it?

1 Answers

Answers 1

Looking at the docs you linked to:

This service can be implemented by the default dialer (see getDefaultDialerPackage()) to allow or disallow incoming calls before they are shown to a user.

Don't think you can do this in a separate app (at least with the current interface: I'd expect in the not too distant feature it will be exposed).

Read More

Sunday, April 2, 2017

ANDROID: email client receiver email id empty in android-parse

Leave a Comment

I'm using android- parse server in app. below is parse db screenshot of email column . the email column is after the hidden password column in database .

parse database screenshot

my problem is


when i retrieve email ids to email client, email is null even if the email column has emails .


note : in the app in another place (another table) i'm pulling email ids to email client in same manner, but there mail is showing well .. only here the problem occurs.

if anyone knows please help ?

this is email column in parse database

 try{                         JSONObject jsonObject = parseObjectToJson(object);                         Log.d("Object", jsonObject.toString());                         Log.d("Email", "+" + object.get("email"));                         personNumber = jsonObject.getString("telephone");                         personEmail = jsonObject.getString("email");                     }catch (JSONException je){                      }catch (ParseException pe){                      } 

this is email button

  emailPerson = (Button)findViewById(R.id.individualEmail);             emailPerson.setOnClickListener(new View.OnClickListener() {                 @Override                 public void onClick(View v) {                     Intent i = new Intent(Intent.ACTION_SEND);                     i.setData(Uri.parse("mailto:"));                     i.setType("plain/text");                     i.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {personEmail});                     startActivity(i);                 }             });             if(personEmail==null || personEmail.equals("")  || personEmail.equals(" ")){                 emailPerson.setClickable(false);                 emailPerson.setEnabled(false);                 emailPerson.setVisibility(View.GONE);             }             else{                 emailPerson.setEnabled(true);                 emailPerson.setClickable(true);                 emailPerson.setVisibility(View.VISIBLE);             } 

here it is working fine but this is a different table in same database . >in this table there is no hidden password field

try{                             corporateEmail = jsonObject.getString("email");                             if(corporateEmail == null || corporateEmail.equals("")){                                 emailCorporate.setVisibility(View.GONE);                                 emailCorporate.setEnabled(false);                                 emailCorporate.setClickable(false);                             } 

emailCorporate = (Button) findViewById(R.id.corporateEmail);         emailCorporate.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 Intent i = new Intent(Intent.ACTION_SEND);                 i.setData(Uri.parse("mailto:"));                 i.setType("plain/text");                 i.putExtra(Intent.EXTRA_EMAIL, new String[] {corporateEmail});                 startActivity(i);             }         }); 

 private JSONObject parseObjectToJson(ParseObject parseObject) throws ParseException, JSONException, com.parse.ParseException {         JSONObject jsonObject = new JSONObject();         parseObject.fetchIfNeeded();         Set<String> keys = parseObject.keySet();         for (String key : keys) {             Object objectValue = parseObject.get(key);             if (objectValue instanceof ParseObject) {                 jsonObject.put(key, parseObjectToJson(parseObject.getParseObject(key)));             } else if (objectValue instanceof ParseRelation) {             } else {                 jsonObject.put(key, objectValue.toString());             }         }         return jsonObject;     } 

1 Answers

Answers 1

if jsonObject is not null check to see if the parse database you are pulling your data from has the the "email" tag

Read More

Thursday, March 16, 2017

Cordova - Notify background running service

Leave a Comment

I am using cordova to build my android application. Since android kills service, i am binding service with a notification to avoid service kill.

Here is my method how i bind the service with notification

@Override public int onStartCommand(Intent intent, int flags, int startId) {     context = this.getApplicationContext();     notifyService();      return START_NOT_STICKY; }  private void notifyService() {     String package_name = this.getApplication().getPackageName();      Intent notificationIntent = new Intent(this, MainActivity.class);     PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);      Bitmap icon = BitmapFactory.decodeResource(getResources(),             this.getApplication().getResources().getIdentifier("icon", "drawable-hdpi", package_name));      Notification notification = new NotificationCompat.Builder(this)             .setContentTitle("Smart Home")             .setContentText("Smart Home running in background") .setSmallIcon(this.getApplication().getResources().getIdentifier("icon", "drawable-hdpi", package_name))             .setContentIntent(pendingIntent)             .setOngoing(true)             .build();      startForeground(notificationId, notification); }  

Here's the output

enter image description here

Notification is generated but notification title is not as i set. Also, when i click this notification, it's moving to app info activity. But i want to move to my main activity.

Does anyone faced this same issue? Or my code need any change for cordova?

2 Answers

Answers 1

when i click this notification, it's moving to app info activity. But i want to move to my main activity to achieve this change this line

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); 

to this line

PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); 

Hope it helps you

Answers 2

Figured it out after a long try. Problem was with my pending intent activity name. Below code worked for me

String package_name = this.getApplication().getPackageName(); Intent notificationIntent = context.getPackageManager().getLaunchIntentForPackage(package_name); 
Read More

Saturday, March 4, 2017

Bypassing Google TTS Engine initialization lag in Android

Leave a Comment

I have tried playing the TextToSpeech object when a specific event is triggered in the phone.

However, I facing issues with the default Google TTS engine that is installed on most phones. As of now, I am playing some text immediately after the TextToSpeech object is initialized, and shutting the resource as soon as the speech is completed, as per the following code:

public class VoiceGenerator { private Context context = null;  private static TextToSpeech voice = null;  public VoiceGenerator(Context context) {     this.context = context; }   public void voiceInit(String text) {     try {         if (voice == null) {              new Thread(new Runnable() {                 @Override                 public void run() {                     voice = new TextToSpeech(context, new TextToSpeech.OnInitListener() {                         @Override                         public void onInit(final int status) {                             try {                                 if (status != TextToSpeech.ERROR) {                                     voice.setLanguage(Locale.US);                                     Log.d("VoiceTTS", "TTS being initialized");                                     HashMap p = new HashMap<String, String>();                                     p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");   //Speaking here                            voice.speak(text, TextToSpeech.QUEUE_ADD, p);                                      voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {                                         @Override                                         public void onStart(String utteranceId) {                                          }                                          @Override                                         public void onDone(String utteranceId) {                                             Log.d("VoiceTTS", "TTS being released");                                             clearTtsEngine();                                         }                                          @Override                                         public void onError(String utteranceId) {                                          }                                     });                                 }                              } catch (Exception e) {                                 clearTtsEngine();                                 Log.d("ErrorLog", "Error occurred while voice play");                                 e.printStackTrace();                             }                           }                     });                 }             }).start();          }     }     catch(Exception e)     {         clearTtsEngine();         Log.d("ErrorLog","Error occurred while voice play");         e.printStackTrace();     } }  public static void clearTtsEngine() {     if(voice!=null)     {         voice.stop();         voice.shutdown();         voice = null;     }     } } 

However, the problem I am facing is the finite amount of delay associated with initializing the Google TTS Engine - about 6-8 seconds on my devices.

I have read on other posts that this delay can be avoided by using other TTS engines. Since I always develop on my Samsung phone, which has its own proprietary TTS configured by default, I never noticed this issue until I checked my app on other brand phones which has the Google TTS engine configured as default. But, I ideally don't want to force users to install another app along with my own, and I hence I would like this to work with the default Google TTS Engine itself.

Through some erroneous coding which I later rectified, I realized that if I could keep the TextToSpeech object initialized beforehand and always not null - once initialized, I could seemingly bypass this delay.

However, since there is a necessity to shutdown the resource once we are done with it, I am not able to keep the object alive and initialized for long, and I do not know when to initialize/shutdown the resource, since I technically need the voice to play anytime the specific event occurs, which mostly would be when my app is not open on the phone.

So my questions are the following :

  1. Can we somehow reduce or eliminate the initialization delay of Google TTS Engine, programmatically or otherwise?

  2. Is there any way through which I can keep the TextToSpeech object alive and initialized at all times like say, through a service? Or would this be a bad, resource-consuming design?

  3. Also is using a static TextToSpeech object the right way to go, for my requirements?

Any solutions along with code would be appreciated.

Update: I have confirmed that the delay is associated exclusively with Google TTS engine, as I have tried using other free and paid TTS engines, wherein there is little or no lag. But I would still prefer to not have any third party dependencies, if possible, and would like to make this work with Google TTS Engine.

UPDATE: I have seemingly bypassed this issue by binding this TTS object to a service and accessing it from the service. The service is STICKY (if the service terminates due to memory issue, Android OS will restart the service when memory is available again) and is configured to restart on reboot of the device.

The service only initializes the TTS object and does no other work. I am not explicitly stopping the service, allowing it to run as long as possible. I have defined the TTS object as a static, so that I can access it from other classes of my app.

Although this seems to be working amazingly well, I am concerned if this could lead to memory or battery issues (in my specific situation where service handles only object initialization and then remains dormant). Is there any problem in my design, or can any further improvements/checks be done for my design?

Manifest file :

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>   <application     android:allowBackup="false"     android:icon="@drawable/ic_launcher"     android:label="@string/app_name" >     <activity         android:name="activity.MainActivity"         android:label="@string/app_name"         android:screenOrientation="portrait" >         <intent-filter>             <action android:name="android.intent.action.MAIN" />              <category android:name="android.intent.category.LAUNCHER" />         </intent-filter>     </activity>      <receiver         android:name="services.BroadcastReceiverOnBootComplete"         android:enabled="true"         android:exported="false">         <intent-filter>             <action android:name="android.intent.action.BOOT_COMPLETED" />         </intent-filter>         <intent-filter>             <action android:name="android.intent.action.PACKAGE_REPLACED" />             <data android:scheme="package" />         </intent-filter>         <intent-filter>             <action android:name="android.intent.action.PACKAGE_ADDED" />             <data android:scheme="package" />         </intent-filter>     </receiver>       <service android:name="services.TTSService"></service> 

BroadcastReceiver code :

public class BroadcastReceiverOnBootComplete extends BroadcastReceiver {  @Override public void onReceive(Context context, Intent intent) {     if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {         Intent serviceIntent = new Intent(context, TTSService.class);         context.startService(serviceIntent);     } } 

}

TTSService code:

public class TTSService extends Service {  private static TextToSpeech voice =null;  public static TextToSpeech getVoice() {     return voice; }  @Nullable @Override  public IBinder onBind(Intent intent) {     // not supporting binding     return null; }  public TTSService() { }  @Override public int onStartCommand(Intent intent, int flags, int startId) {      try{         Log.d("TTSService","Text-to-speech object initializing");          voice = new TextToSpeech(TTSService.this,new TextToSpeech.OnInitListener() {             @Override             public void onInit(final int status) {                 Log.d("TTSService","Text-to-speech object initialization complete");                                 }             });      }     catch(Exception e){         e.printStackTrace();     }       return Service.START_STICKY; }  @Override public void onDestroy() {     clearTtsEngine();     super.onDestroy();  }  public static void clearTtsEngine() {     if(voice!=null)     {         voice.stop();         voice.shutdown();         voice = null;     }    } } 

Modified VoiceGenerator code:

public class VoiceGenerator {  private TextToSpeech voice = null;  public VoiceGenerator(Context context) {     this.context = context; }   public void voiceInit(String text) {    try {         if (voice == null) {              new Thread(new Runnable() {                 @Override                 public void run() {                      voice = TTSService.getVoice();                     if(voice==null)                         return;                      voice.setLanguage(Locale.US);                     HashMap p = new HashMap<String, String>();                     p.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "ThisUtterance");                     voice.speak(text, TextToSpeech.QUEUE_ADD, p);                      voice.setOnUtteranceProgressListener(new UtteranceProgressListener() {                         @Override                         public void onStart(String utteranceId) {                          }                          @Override                         public void onDone(String utteranceId) {                         }                          @Override                         public void onError(String utteranceId) {                          }                     });                 }             }).start();          }     }     catch(Exception e)     {         Log.d("ErrorLog","Error occurred while voice play");         e.printStackTrace();     } }     } 

1 Answers

Answers 1

I'm the developer of the Android application utter! That isn't a shameless plug, it's to demonstrate that I use the design pattern you are considering and I've 'been through' what has prompted your question.

It's fresh in my mind, as I've spent the last year rewriting my code and had to give great consideration to the surrounding issue.

  • Can we somehow reduce or eliminate the initialization delay of Google TTS Engine, programmatically or otherwise?

I asked a similar question some time ago and initialising the Text to Speech object on a background thread where it is not competing with other tasks, can reduce the delay slightly (as I see you are already doing in your posted code).

You can also make sure that the request to speak is not being delayed further by selecting an embedded voice, rather than one dependent on a network:

In API 21+ check out the options on the Voice class. Particularly getFeatures() where you can examine the latency and requirement for a network.

In API <21 - Set the KEY_FEATURE_NETWORK_SYNTHESIS to false inside your parameters.

Regardless of the above, the Google TTS Engine has the longest initialisation time of any of the engines I've tested (all of them I think). I believe this is simply because they are using all available resources on the device to deliver the highest quality voice they can.

From my own personal testing, this delay is directly proportional to the hardware of the device. The more RAM and performant the processor, the less the initialisation time. The same came be said for the current state of the device - I think you'll find that after a reboot, where there is free memory and Android will not need to kill other processes, the initialisation time will be reduced.

In summary, other than the above mentioned, no, you cannot reduce the initialisation time.

  • Is there any way through which I can keep the TextToSpeech object alive and initialized at all times like say, through a service? Or would this be a bad, resource-consuming design?

  • Also is using a static TextToSpeech object the right way to go, for my requirements?

As you've noted, a way to avoid the initialisation time, is to remain bound to the engine. But, there are further problems that you may wish to consider before doing this.

If the device is in a state where it needs to free up resources, which is the same state that causes an extended initialisation delay, Android is well within its rights to garbage collect this binding. If you hold this binding in a background service, the service can be killed, putting you back to square one.

Additionally, if you remain bound to the engine, your users will see the collective memory usage in the Android running application settings. For the many, many users who incorrectly consider (dormant) memory usage directly proportional to battery drain, from my experience, this will cause uninstalls and poor app ratings.

At the time of writing, Google TTS is bound to my app at a cost of 70mb.

If you still want to proceed on this basis, you can attempt to get Android to prioritise your process and kill it last - You'd do this by using a Foreground Service. This opens another can of worms though, which I won't go into.

Effectively, binding to the engine in a service and checking that service is running when you want the engine to speak, is a 'singleton pattern'. Making the engine static within this service would serve no purpose that I can think of.

Finally, to share my experience as to how I've dealt with the above.

I have 'Google is slow to initialise' at the top of my 'known bugs' and 'FAQ' in the application.

I monitor the time it takes for the engine to call onInit. If it's taking too long, I raise a notification to the user and direct them to the FAQ, where they are gently advised to try another TTS engine.

I run a background timer, that releases the engine after a period of inactivity. This amount of time is configurable by the user and comes with initialisation delay warnings...

I know the above doesn't solve your problems, but perhaps my suggestions will pacify your users, which is a distant second to solving the problem, but hey...

I've no doubt Google will gradually increase the initialisation performance - Four years ago, I was having this problem with IVONA, who eventually did a good job on their initialisation time.

Read More

Sunday, January 8, 2017

Implement emojis in android keyboard with popupwindow

Leave a Comment

I've developed a keyboard and now i need to add emojis to it , from other questions i've realized the best way is with popupwindow,

Here's what i've done:

  case -102:             LayoutInflater layoutInflater                     = (LayoutInflater)getBaseContext()                     .getSystemService(LAYOUT_INFLATER_SERVICE);             View popupView = layoutInflater.inflate(R.layout.emoji_view, null);             final PopupWindow popupWindow = new PopupWindow(                     popupView,                     LinearLayout.LayoutParams.MATCH_PARENT,                     LinearLayout.LayoutParams.MATCH_PARENT);             popupWindow.showAsDropDown(getWindow().getOwnerActivity().getCurrentFocus(),50, -30); 

Unfortunatly this doesn't work , showAsDropDown needs a view as its first var , and if the keyboard is in another app i don't have a view to give him...

Is there a way to fix that ? or am i going about it all wrong and there is a better way...

all help will be appreciated!

3 Answers

Answers 1

Given that you are using the official SoftKeyBoard implementation as a template for your keyboard:

//Cut some pieces of the code for clarity case -102:     LayoutInflater layoutInflater = (LayoutInflater)getBaseContext()         .getSystemService(LAYOUT_INFLATER_SERVICE);     View popupView = layoutInflater.inflate(R.layout.emoji_view, null);     PopupWindow popupWindow = new PopupWindow(popupView, MATCH_PARENT, MATCH_PARENT);     popupWindow.showAsDropDown(mInputView); 

Use the view that you have inflated for your keyboard, in the offical SoftKeyBoard and the snippet above it is called mInputView.

Answers 2

you can refer this github link for complete code

EmojiIcon

Answers 3

hi i have done same things. I have made one custom keyboard in android.

EmoticonsPagerAdapter emojiAdapter;  /**  * Defining all components of emoticons keyboard  */ private void enablePopUpView() {      final ViewPager pager = (ViewPager) popUpView             .findViewById(R.id.emoticons_pager);     pager.setOffscreenPageLimit(3);      final ArrayList<EmojiItem> paths = EmojiUtil.getInstance(acitiviy)             .getAllEmojis();     final ArrayList<EmojiItem>[] groups = new ArrayList[5];     for (EmojiItem emoji : paths) {         if (groups[emoji.emojiGroup] == null) {             groups[emoji.emojiGroup] = new ArrayList<EmojiItem>();         }         groups[emoji.emojiGroup].add(emoji);     }     final ArrayList<EmojiItem> history = new ArrayList<EmojiItem>();     ArrayList<Integer> historyIds = SettingsUtil.getHistoryItems(acitiviy);     for (Integer his : historyIds) {         for (EmojiItem emoji : paths) {             if (emoji.id == his) {                 history.add(emoji);                 break;             }         }     }     history.add(paths.get(0));      final KeyClickListener onEmojiClick = new KeyClickListener() {          @Override         public void keyClickedIndex(EmojiItem index) {              int cursorPosition = editMessage.getSelectionStart();             editMessage.getText().insert(cursorPosition, index.emojiText);             try {                 editMessage.getText().setSpan(                         new ImageSpan(index.emojiDrawable), cursorPosition,                         cursorPosition + 1,                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);             } catch (Exception e) {             }             if (history.get(0) != index)                 history.add(0, index);             SettingsUtil.setHistoryItems(acitiviy, history);             emojiAdapter.notifyDataSetChanged();             pager.setAdapter(emojiAdapter);         }     };      ((ImageButton) popUpView.findViewById(R.id.emoji2))             .setImageDrawable(groups[0].get(0).emojiDrawable);     ((ImageButton) popUpView.findViewById(R.id.emoji3))             .setImageDrawable(groups[1].get(0).emojiDrawable);     ((ImageButton) popUpView.findViewById(R.id.emoji4))             .setImageDrawable(groups[2].get(0).emojiDrawable);     ((ImageButton) popUpView.findViewById(R.id.emoji5))             .setImageDrawable(groups[3].get(0).emojiDrawable);     ((ImageButton) popUpView.findViewById(R.id.emoji6))             .setImageDrawable(groups[4].get(0).emojiDrawable);     popUpView.findViewById(R.id.emoji1).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = history;                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });     popUpView.findViewById(R.id.emoji2).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = groups[0];                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });     popUpView.findViewById(R.id.emoji3).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = groups[1];                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });     popUpView.findViewById(R.id.emoji4).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = groups[2];                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });     popUpView.findViewById(R.id.emoji5).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = groups[3];                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });     popUpView.findViewById(R.id.emoji6).setOnClickListener(             new OnClickListener() {                  @Override                 public void onClick(View v) {                     emojiAdapter.emojis = groups[4];                     emojiAdapter.notifyDataSetChanged();                     pager.setAdapter(emojiAdapter);                 }             });      emojiAdapter = new EmoticonsPagerAdapter(acitiviy, groups[0],             onEmojiClick);     pager.setAdapter(emojiAdapter);      // Creating a pop window for emoticons keyboard     popupWindow = new PopupWindow(popUpView, LayoutParams.MATCH_PARENT,             (int) keyboardHeight, false);      View backSpace = (View) popUpView.findViewById(R.id.imageBackspace);     backSpace.setOnClickListener(new OnClickListener() {          @Override         public void onClick(View v) {             KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0,                     0, 0, 0, KeyEvent.KEYCODE_ENDCALL);             editMessage.dispatchKeyEvent(event);         }     });      popupWindow.setOnDismissListener(new OnDismissListener() {          @Override         public void onDismiss() {             emoticonsCover.setVisibility(LinearLayout.GONE);         }     });      ViewPager pagerStickers = (ViewPager) popUpView             .findViewById(R.id.stickers_pager);     pagerStickers.setOffscreenPageLimit(3);  }  private void showKeyboardPopup(View root, boolean attaches) {     if (!popupWindow.isShowing()) {         popupWindow.setHeight((int) (keyboardHeight));          if (isKeyBoardVisible) {             imageEmoji.setImageResource(R.drawable.emoji_kbd);             emoticonsCover.setVisibility(LinearLayout.GONE);          } else {             imageEmoji.setImageResource(R.drawable.ic_down);             emoticonsCover.setVisibility(LinearLayout.VISIBLE);         }         try {             popupWindow.showAtLocation(root, Gravity.BOTTOM, 0, 0);         } catch (Exception e) {         }     } else {         imageEmoji.setImageResource(R.drawable.emoji_btn_normal);         popupWindow.dismiss();         return;     }      imageAttaches.setBackgroundColor(attaches ? 0xFF808080 : 0x00000000);     imageEmojis.setBackgroundColor(attaches ? 0x00000000 : 0xFF808080);     imageStickers.setBackgroundColor(0x00000000);     layoutEmojis.setVisibility(attaches ? View.GONE : View.VISIBLE);     layoutStickers.setVisibility(View.GONE);  } 

Please checkout for more details click here.

Thanks hope this will help you.It is bit old but you can try it.

Read More

Thursday, March 17, 2016

Android service not restarting in lollipop

Leave a Comment

In my application, I use location based service in background. So I need to restart my service when it gets destroyed.

But I got this message in logcat

Spurious death for ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391}, curProc for 20614: null

My service onTaskRemoved

@Override public void onTaskRemoved(Intent rootIntent) {     System.out.println("onTaskRemoved called");     Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());     restartServiceIntent.setPackage(getPackageName());      PendingIntent restartServicePendingIntent = PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);     AlarmManager alarmService = (AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);     alarmService.set(             AlarmManager.ELAPSED_REALTIME,             SystemClock.elapsedRealtime() + 1000,             restartServicePendingIntent);  } 

My service onDestroy

@Override public void onDestroy() {     System.out.println("destroy service");     super.onDestroy();     wakeLock.release(); } 

My service onStartCommand

@Override public int onStartCommand(Intent intent, int flags, int startId) {         return Service.START_STICKY; } 

I don`t know what is the error. I searched both in google & stackoverflow. All of them refer Service.START_STICKY. but I already used it.

Same service restart works in KitKat, but with some delay(~5 mins).

Any help is appreciated.

6 Answers

Answers 1

Your code in onTaskRemoved is preventing the system to run the killProcess commands. The delay on Kitkat is caused by using alarmService.set, which is inexact from API 19. Use setExact instead.

If you have a service that you want to keep alive, It is recommended that you attach a notification to it and make it foreground. That way the likeliness of it being killed would be lowered.

Answers 2

import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.os.Environment; import android.os.IBinder; import android.support.v7.app.NotificationCompat;  import java.io.File; import java.io.IOException;  import activity.MainActivity; import activity.R; import fragment.MainFragment;  public class MyService extends Service {     public static final int NOTIFICATION_CODE = 1;       @Override     public void onCreate() {         super.onCreate();       }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         startForeground(NOTIFICATION_CODE, getNotification());         return START_STICKY;     }      @Override     public IBinder onBind(Intent intent) {         return null;     }      @Override     public void onDestroy() {         stopForeground(true);         super.onDestroy();     }      @Override     public boolean stopService(Intent name) {         return super.stopService(name);     }       /**      * Create and return a simple notification.      */     private Notification getNotification() {             Notification notification;         NotificationCompat.Builder builder = new NotificationCompat.Builder(this);         builder.setColor(getResources()                         .getColor(R.color.material_deep_teal_500))                 .setAutoCancel(true);          notification = builder.build();         notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;          return notification;     }   } 

You can modify this code to accomodate your needs but this is the basic structure to start foreground service. Which restarts if gets killed.

Answers 3

You can restart it by using a BroadcasteReceiver which handles the broadcast sent from onDestroy() of your service.

How to do this:

StickyService.java

public class StickyService extends Service {      @Override     public IBinder onBind(Intent arg0) {         return null;     }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         return START_STICKY;     }      @Override     public void onDestroy() {         super.onDestroy();         sendBroadcast(new Intent("IWillStartAuto"));     }  } 

RestartServiceReceiver.java

public class RestartServiceReceiver extends BroadcastReceiver {      @Override     public void onReceive(Context context, Intent intent) {     context.startService(new Intent(context.getApplicationContext(), StickyService.class));      }  } 

Declare the components in manifest file:

    <service android:name=".StickyService" >     </service>      <receiver android:name=".RestartServiceReceiver" >         <intent-filter>             <action android:name="IWillStartAuto" >             </action>         </intent-filter>     </receiver> 

Hope this will help you.

Answers 4

how you check issocketalive that socket is connected or not ? if sockettimeoutexception is generated then try to on set getinputstream and getoutputstream. other issue that may be socket not closed properly. So if possible then put your socket code here

Answers 5

this worked for me

Add this attribute in android:allowBackup="false" in manifest file in application tag.

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">  <application     android:allowBackup="false"     tools:replace="android:allowBackup">  </application> </manifest> 

Answers 6

The idea of having a service ALWAYS running in background in Android is just wrong 99% of the times.

The system need to "shut down" CPU, and switch to a low battery usage profile.

You are saying you have a location based service. I assume you are using Google Play Services FusedLocationProvider, if not you should.

The FusedLocationProvider allow you to register for location changes using a PendingIntent. Meaning your services doesn't need to run all the time, it just need to register for location changes and then react when a new location come and do its stuff.

See the FusedLocationProviderApi official documentation.

To start listening for location updates

  1. connect to the GoogleClient using the LocationServices.API API
  2. Build your LocationRequest according to your needs (see the doc)
  3. Call requestLocationUpdates() using the PendingIntent version

To stop listening

  1. connect to the GoogleClient using the LocationServices.API API
  2. Call removeLocationUpdates() using the same PendingIntent

Your PendingIntent can launch another service to handle the new location.

For example doing this from a service:

public void startMonitoringLocation(Context context) {     GoogleApiClient client = new GoogleApiClient.Builder(context)                  .addApi(LocationServices.API)                  .build()     ConnectionResult connectionResult = mApiClient.blockingConnect();     if (connectionResult.isSuccess()) {         LocationServices.FusedLocationApi                 .requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));     } else {         handleConnectionFailed(context);     } } 

Then the service can immediately stop.

The first time this code run it WILL fail. The connection to the google client usually require the user to take some actions. The ConnectionResult.hasResolution() method will return true if this is the case. Otherwise the reason is something else and you can't recover from it. Meaning the only thing you can do is inform the user the feature will not work or have a nice fallback.

The ConnectionResult.getResolution() give you a PendingIntent you need to use an Activity and startIntentSenderForResult() method on the Activity to resolve this intent. So you would create a Notification starting your Activity to resolve that, and in the end call your Service again.

I usually just start an Activity dedicated to do all the work. It's lot easier but you don't want to call connectBlocking() in it. Check out this on how to do it.

You may ask why not requesting location updates directly in the Activity. That's actually perfectly fine, unless you need the location monitor to automatically start with the device, even if the user didn't explicitly opened the App.

<receiver android:name=".BootCompletedBroadcastReceiver">     <intent-filter>         <action android:name="android.intent.action.BOOT_COMPLETED" />     </intent-filter> </receiver> 

This way you can just run your service to connect and request location updates when the device is rebooted.

Example on how you can build your location request:

    public LocationRequest buildLocationRequest() {         LocationRequest locRequest = LocationRequest.create();         // Use high accuracy         locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);         // how often do you need to check for the location         // (this is an indication, it's not exact)         locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);         // if others services requires the location more often         // you can still receive those updates, if you do not want         // too many consider setting this lower limit         locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);         // do you care if the user moved 1 meter? or if he move 50? 1000?         // this is, again, an indication         locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);         return locRequest;     } 

And your pending intent:

public PendingIntent buildPendingIntent(Context context) {     Intent intent = new Intent(context, LocationUpdateHandlerService.class);     intent.setAction(ACTION_LOCATION_UPDATE);     intent.setPackage(context.getPackageName());     return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT); } 

Your LocationUpdateHandlerService can be an IntentService if you need to do work in background:

@Override protected void onHandleIntent(Intent intent) {     if (intent != null) {         Bundle extras = intent.getExtras();         if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {             Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);             handleLocationChanged(location);         } else {             Log.w(TAG, "Didn't receive any location update in the receiver");         }      } } 

But can also be a Broadcast or anything that suits you.

Read More