Hang up and decline incoming call by custom activity[buttons] Android - java

I am just in process by learning Android a bit, and i have stumbled on this problem.
I want to do a "custom incoming call screen". My current Solution is a class(IncomingCallInterceptor) that extends from BroadcastReceiver. In IncomingCallInterceptor class i override the onReceive and starting my activity(MainActivity) with layout when the phone is ringing.
In that activity(MainActivity) i have three buttons:
Accept Call, Hang Up, Decline Call
Those buttons should do what they say, Answer the phone, hang up the phone or decline the call.
I have in someway got the Accept Call to work, but not Hang Up and Decline.
Heres my code below:
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.uppgift.six.one.incoming61.sixone" >
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name="IncomingCallInterceptor">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
</activity>
</application>
</manifest>
IncomingCallInterceptor that extends from BroadcastReceiver:
public class IncomingCallInterceptor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Context appContext = context.getApplicationContext();
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String msg = "Phone state changed to " + state;
if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
String incomingNumber = intent.getStringExtra
(TelephonyManager.EXTRA_INCOMING_NUMBER);
msg += ". Incoming number is this " + incomingNumber;
//START MY ACTIVITY!
Intent i = new Intent(appContext, MainActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(i);
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
}
Here is my Activity(The Layout is nothing to post, just now is basically three buttons)
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnAnswer = (Button)findViewById(R.id.btnAnswer);
Button btnDecline= (Button)findViewById(R.id.btnDecline);
Button btnHangUp= (Button)findViewById(R.id.btnHangUp);
btnAnswer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
KeyEvent.KEYCODE_HEADSETHOOK));
sendOrderedBroadcast(i, null);
}
});
btnDecline.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Decline Call (I need help here)
}
});
btnHangUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Hang Up Call (I need help here)
}
});
}
In the MainActivity class it is marked by comments where i need some help.
I have also seen something about a "Telephonyservice interface"(thingy) solution, but i don't understand how that worked when i was testing it.

Reject Call:
try {
TelephonyManager tm = (TelephonyManager)
getSystemService(Context.TELEPHONY_SERVICE);
try {
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
Object telephonyService = m.invoke(tm); // Get the internal ITelephony object
c = Class.forName(telephonyService.getClass().getName()); // Get its class
m = c.getDeclaredMethod("endCall"); // Get the "endCall()" method
m.setAccessible(true); // Make it accessible
m.invoke(telephonyService); // invoke endCall()
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
}
}

Related

Is sendBroadcast() the best way to keep a WakefulBroadcastReceiver alive in Android?

I try do do some background calculation tasks in an Android application.
My Main class :
public class MainActivity extends AppCompatActivity {
private CalculationReceiver calculationReceiver = new CalculationReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
calculationReceiver.doAddition(mContext, 2, 2);
}
});
}
}
My service :
public class CalculationService extends IntentService {
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
CalculationReceiver.completeWakefulIntent(intent);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
}
My receiver :
public class CalculationReceiver extends WakefulBroadcastReceiver {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, CalculationService.class);
int receiverNumber1 = intent.getIntExtra(NUMBER_1,0);
int receiverNumber2 = intent.getIntExtra(NUMBER_2,0);
service.putExtra(NUMBER_1,receiverNumber1);
service.putExtra(NUMBER_2,receiverNumber2);
startWakefulService(context, service);
}
public void doAddition (Context context, int number1, int number2){
Intent intent = new Intent(context, CalculationReceiver.class);
intent.putExtra(NUMBER_1,number1);
intent.putExtra(NUMBER_2,number2);
context.sendBroadcast(intent);
}
}
My Manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testservices">
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".ReductionService"
android:enabled="true" />
<receiver android:name=".ReductionReceiver"/>
<service android:name=".CalculationService"
android:enabled="true" />
<receiver android:name=".CalculationReceiver"/>
</application>
</manifest>
The calculations of the application are more complex than these additions, and can take several minutes (in average 15 minutes) to be done.
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
The idea is that the user starts his calculation and then waits for the application to give the result. In the meantime, he can launch other apps or lock his phone, the calculation must not stop.
This approach seems to work.
What bothers me here is the call to service in the receiver:
context.sendBroadcast (intent);
Is there a more "clean" way to start the service?
What strikes me is that it does not seem very "clean", especially the passage of several times the same parameter (number1 and number2)
Thanks
According to the Google documentation (https://developer.android.com/training/scheduling/wakelock.html), I decided to implement this architecture to make sure that the calculation is done to the end.
That is not how the documentation shows using WakefulBroadcastReceiver. Plus, WakefulBroadcastReceiver was deprecated in version 26.0.0 of the support libraries.
The idea is that the user starts his calculation and then waits for the application to give the result.
My interpretation of this is that the user is requesting, through your activity's UI, to start the calculation. This means that at this point in time, the screen is on and you have an activity in the foreground.
Is there a more "clean" way to start the service?
Call startService().
Step #1: Delete your use of the deprecated WakefulBroadcastReceiver
Step #2: Have your activity call startService() to start the service
Step #3: Have your service acquire a partial WakeLock through the PowerManager system service, in the service's onCreate() method
Step #4: Have your service release that WakeLock in the service's onDestroy() method
Step #5: Modify the service to be a foreground service, calling startForeground() in onCreate() with a suitable Notification to allow the user to control the behavior of the service
Note that:
If you skip Step #5, your service will stop running after ~1 minute on Android 8.0+.
This will still not work on Android 6.0+ if the device enters into Doze mode. That should not happen for ~1 hour, but you need to make sure that your calculations are done by then.
Consider offloading the calculation work to a server, rather than burning up the user's CPU for an extended period of time (through your calculation work plus the wakelock)
It was in this way that I resolved it, following the advice of #CommonsWare
Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final Context mContext = this;
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent service = new Intent(mContext, CalculationService.class);
service.putExtra(NUMBER_1,2);
service.putExtra(NUMBER_2,2);
startService(service);
}
});
}
}
Calculation Service :
public class CalculationService extends IntentService {
public static final String NUMBER_1 = "NUMBER_1";
public static final String NUMBER_2 = "NUMBER_2";
private static final int FOREGROUND_ID = 42;
PowerManager.WakeLock wakeLock;
public CalculationService() {
super("Calculation Service");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
int nb1 = intent.getIntExtra(NUMBER_1,0);
int nb2 = intent.getIntExtra(NUMBER_2,0);
doAddition(nb1,nb2);
}
public void doAddition(int number1, int number2){
int result = number1+number2;
System.out.println("Result : " + result);
}
#Override
public void onCreate() {
super.onCreate();
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"CalculationServiceWakelockTag");
wakeLock.acquire();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Calculation App")
.setContentText("Calculation in progress")
.setContentIntent(pendingIntent).build();
startForeground(FOREGROUND_ID, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
wakeLock.release();
}
}
This is working great :)
But can I call
startForegroundService(service)
instead of
startService(service);
or not ? And why ?

Android Simple App loading pictures not working

sorry for the vague title, i really dont know what i did wrong in my app. Am new to android, so bear with me.
Here ist the Manifest.xml :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.phamt.ubung11">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:debuggable="true"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="com.example.loadpicservice" />
</intent-filter>
</receiver>
<service
android:name=".LoadPicService"
android:enabled="true"
android:exported="true"></service>
</application>
</manifest>
And my MainActivity Class:
public class MainActivity extends AppCompatActivity {
private static String url1 = "https://lh3.googleusercontent.com/j5fD3m5qXRNtGYDuajhEtS_etFvU8FE5PogmqTY2hrshDG0_urf_UBeVAyJljoCxdf4=w300";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button buttonGo = (Button) findViewById(R.id.button4);
buttonGo.setOnClickListener(
new Button.OnClickListener() {
public void onClick (View v) {
TextView textView = (TextView) findViewById(R.id.textView1);
textView.setText("Now Click Kill Button to delete pictures");
Intent intent = new Intent(MainActivity.this, LoadPicService.class);
intent.putExtra("url", MainActivity.url1);
startService(intent);
}
}
);
Button buttonKill = (Button) findViewById(R.id.button);
buttonKill.setOnClickListener(
new Button.OnClickListener() {
public void onClick( View v) {
System.exit(0);
}
}
);
MyReceiver receiver = new MyReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
byte[] byteArray = getIntent().getByteArrayExtra("bild");
Bitmap bmp = BitmapFactory.decodeByteArray(byteArray,0,byteArray.length);
//funktion SetImage here !
ImageView img1 = (ImageView) findViewById(R.id.imageView2);
img1.setImageBitmap(bmp);
img1.setVisibility(View.VISIBLE);
}
};
IntentFilter intentFilter = new IntentFilter("com.example.loadpicservice");
registerReceiver(receiver,intentFilter);
}
}
And last, my Service Class.
public class LoadPicService extends IntentService {
public LoadPicService() {
super("LoadService");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
String urls = intent.getStringExtra("url");
Intent ansIntent = new Intent(this, LoadPicService.class);
Bitmap bmp = null;
try {
URL url = new URL(urls);
bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (Exception e) {
Log.i("cannot download picture", "CANT DOWNLOAD PICTURE");
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
ansIntent.putExtra("bild",byteArray);
ansIntent.setAction("com.example.loadpicservice");
//ggbf. runterskalieren.
sendBroadcast(ansIntent);
}
public void onStartCommand(Intent intent, int startId) {
}
}
So in short, here are the things i did.
I create Service, which started when you click button Go, which sends an Intent with URL as Extra.
On starting the Service loads the picture and convert it into ByteArray and send as Extra to MainActivity through a new Intent
In the MainActivity, on receiving the broadcast, convert the given ByteArray to BitMap and set current ImageView to that picture.
EDIT : The picture couldnt be loaded, i checked if the url is alright. Otherwise the OnClick Method works.
What did i do wrong here ? Thanks for any help!

Android app opens after a few seconds after pressing home button during splash screen

I am having an issue I can't seem to figure out the reason for.
When you launch the app, a splash screen is first displayed for 2.5 seconds before finishing and starting a new activity. If you press the home or back button during this time the app will close as normal. However after a few seconds (longer than 2.5) the app will open and start from the activity that comes after the splash screen.
Any help on why this happens is appreciated!
Here is the implementation of the Splash screen (I do however not believe anything here causes this issue as I've tried different implementations)
`public class SplashScreenActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
Thread myThread = new Thread(){
#Override
public void run() {
try {
sleep(2500);
Intent intent = new Intent(getApplicationContext(),MainActivity.class);
startActivity(intent);
finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
myThread.start();`
Here's the manifest
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".activities.MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar"
android:launchMode = "singleInstance">
</activity>
<activity android:name=".activities.SplashScreenActivity"
android:theme="#style/Theme.AppCompat.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".alert.BroadCaster" >
</receiver>
<service android:name=".timer.TimerService"
android:process=":timerservice" />
</application>
It happens because you are creating a new Thread and this thread will be still alive after you put your app in background. You can change your approach using an Handler. If you need that your next Activity won't start if the splash screen is in background, you have to store the current time before the delay starts.
private static final long SPLASH_SCREEN_MS = 2500;
private long mTimeBeforeDelay;
private Handler mSplashHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
// Create a new Handler.
mSplashHandler = new Handler();
}
#Override
protected void onResume() {
super.onResume();
// The first time mTimeBeforeDelay will be 0.
long gapTime = System.currentTimeMillis() - mTimeBeforeDelay;
if (gapTime > SPLASH_SCREEN_MS) {
gapTime = SPLASH_SCREEN_MS;
}
mSplashHandler.postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(intent);
SplashScreenActivity.this.finish();
}
}, gapTime);
// Save the time before the delay.
mTimeBeforeDelay = System.currentTimeMillis();
}
#Override
protected void onPause() {
super.onPause();
mSplashHandler.removeCallbacksAndMessages(null);
}
Just use handler instead of thread sleep like this
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
Intent intent = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(intent);
SplashScreenActivity.this.finish();
}
}, SPLASH_DURATION);
You need to implement the onStop() method, only if you want to save data and memory.

android read USSD response

I'm trying to read USSD response to get Sim balance amount etc, and I'm having issues, I have been reading many questions related to that on Stackoverflow, nothing has worked so far. Except for this that came close: Prevent USSD dialog and read USSD response?. by #HenBoy331
But i'm still having issues with it. My broadcast receiver doesn't get called too. I'm using 4.4.2
But it shows nothing. I can't seem to parse the message and get the balance.
I have a MainActivity to make the phone call, ReceiverActivity to implement broadcast receiver, USSDService class to get the USSD response.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, USSDService.class));
dailNumber("100");
}
private void dailNumber(String code) {
String ussdCode = "*" + code + Uri.encode("#");
startActivity(new Intent("android.intent.action.CALL", Uri.parse("tel:" + ussdCode)));
}
}
RecieverActivity.java
public class RecieverActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter("com.times.ussd.action.REFRESH");
this.registerReceiver(new Receiver(), filter);
}
public class Receiver extends BroadcastReceiver {
private String TAG = "XXXX";
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
Log.i(TAG, "Got message: " + message);
}
}
}
USSDService.java
public class USSDService extends AccessibilityService {
public String TAG = "XXXX";
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.d(TAG, "onAccessibilityEvent");
AccessibilityNodeInfo source = event.getSource();
/* if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !event.getClassName().equals("android.app.AlertDialog")) { // android.app.AlertDialog is the standard but not for all phones */
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !String.valueOf(event.getClassName()).contains("AlertDialog")) {
return;
}
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && (source == null || !source.getClassName().equals("android.widget.TextView"))) {
return;
}
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && TextUtils.isEmpty(source.getText())) {
return;
}
List<CharSequence> eventText;
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
eventText = event.getText();
} else {
eventText = Collections.singletonList(source.getText());
}
String text = processUSSDText(eventText);
if( TextUtils.isEmpty(text) ) return;
// Close dialog
performGlobalAction(GLOBAL_ACTION_BACK); // This works on 4.1+ only
Log.d(TAG, text);
// Handle USSD response here
Intent intent = new Intent("com.times.ussd.action.REFRESH");
intent.putExtra("message", text);
sendBroadcast(intent);
}
private String processUSSDText(List<CharSequence> eventText) {
for (CharSequence s : eventText) {
String text = String.valueOf(s);
// Return text if text is the expected ussd response
if( true ) {
return text;
}
}
return null;
}
#Override
public void onInterrupt() {
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
Log.d(TAG, "onServiceConnected");
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.flags = AccessibilityServiceInfo.DEFAULT;
info.packageNames = new String[]{"com.android.phone"};
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
setServiceInfo(info);
}
}
AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dialussd">
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".services.USSDService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="#xml/config_service" />
</service>
<receiver android:name=".RecieverActivity$Receiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
Please, is there any way i'm implementing this wrongly, Or perhaps there is a new way to get USSD responses which works?
After the launch, change the settings manually
Setting->Accessibility Setting -> You can see a option 'your app name'. Turn it on. (This has to be done from as a part of application flow(not manual))
instead of using broadcast receiver use these two lines of code in
add this code in MainActivity
public static void setTextViewToModify(String Text) {
textView.setText(Text);}
and add this in service class with in onAccessibityEvent
MainActivity.setTextViewToModify(text);

Acquiring and storing accelerometer data on Android Wear smartwatch

I'm trying to acquire accelerometer data using an Android Wear app on a smartwatch (Samsung Gear Live). To acquire that data I use a service which listens to three components:
a SensorEventListener, where the onSensorChanged method triggers a SensorEventLoggerTask that stores the accelerometer data with timestamps in a file on the smartwatch
a System intent that listens to the battery
an Intent that listens button clicks on an activity. The activity is used to annotate the data, so I have an idea at what timestamp an (person)activity (running, eating, sleeping,...) was started:
This works fine and I can sample the data every (more or less) 5-6 ms. However this only works when the smartwatch is connected through adb or when the activity is active (to press a button). From the moment it is not connected anymore, there are gaps in the data. With gaps I mean that the time between two timestamps of accelerometer values is much larger than the 5-6 ms. Going to seconds... The gaps appear irregular. But when the activity becomes active (to press a button) or I connect the smartwatch to the adb, the values are gathered. It appears that the service is paused/sleeps/shut down, when the smartwatch is not active.
Below I post the code. The project consists of two classes WearableActivity and WearableService. Furthermore, I also show the Manifest. Allthough this is only prototyping code, any help in resolving the data gaps or suggestions to the code, would be greatly appreciated.
WearableActivity.java
public class WearableActivity extends Activity {
//Set strings and widgets
private static final String TAG = "WearableActivity";
private TextView mTextView;
private Button[] buttons;
private String[] strings;
#Override
protected void onCreate(Bundle savedInstanceState) {
//setscreen
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wearable);
//check if service is already running
if(this.check() == false)
this.startService(new Intent(this, WearableService.class));
Resources res = this.getResources();
strings = res.getStringArray(R.array.button_names);
buttons = new Button[strings.length];
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub stub) {
mTextView = (TextView) stub.findViewById(R.id.text);
mTextView.setText("ActiMon_Store Started");
buttons[0] = (Button)stub.findViewById(R.id.button1);
buttons[1] = (Button)stub.findViewById(R.id.button2);
buttons[2] = (Button)stub.findViewById(R.id.button3);
buttons[3] = (Button)stub.findViewById(R.id.button4);
buttons[0].setText(strings[0]);
buttons[1].setText(strings[1]);
buttons[2].setText(strings[2]);
buttons[3].setText(strings[3]);
for(Button b : buttons)
b.setOnClickListener(btnListener);
}
});
}
//---create an anonymous class to act as a button click listener---
private OnClickListener btnListener = new OnClickListener()
{
public void onClick(View v)
{
Intent buttonIntent = new Intent("button_activity");
Button b = (Button)v;
buttonIntent.putExtra("time", Long.toString(System.currentTimeMillis()));
buttonIntent.putExtra("button", b.getText().toString());
sendBroadcast(buttonIntent);
Toast.makeText(getApplicationContext(),
"Event sent", Toast.LENGTH_LONG).show();
}
};
//Checks is the service WearableService already started
public boolean check(){
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
{
if ("com.example.WearableService"
.equals(service.service.getClassName()))
{
Log.i(TAG,"Service already running!");
return true;
}
}
Log.i(TAG,"Service already running!");
return false;
}
}
WearableService.java
public class WearableService extends Service implements SensorEventListener {
private PrintStream ps;
private PrintStream ps_bat;
private PrintStream ps_button;
private String androidpath;
private float[] gravity = new float[3];
private float[] linear_acceleration = new float[3];
private PowerManager pm;
private PowerManager.WakeLock wl;
private SensorManager mSensorManager;
private Sensor mAcceleroSensor;
//Create the service and register the listeners
#Override
public void onCreate(){
super.onCreate();
this.registerListeners();
}
//Registers the SensorManager, the listener to the battery and the intent
private void registerListeners(){
mSensorManager = (SensorManager)this.getSystemService(SENSOR_SERVICE);
mAcceleroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this,mAcceleroSensor,SensorManager.SENSOR_DELAY_FASTEST);
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(battery_receiver, ifilter);
this.registerReceiver(broadcastReceiver, new IntentFilter("button_activity"));
}
//Start service as at STICKY
#Override
public int onStartCommand(Intent intent, int flags, int startId){
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
this.getFile();
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE,"KeepCPUWorking");
wl.acquire();
return Service.START_STICKY;
}
//When sensor value has changed
public void onSensorChanged(SensorEvent event){
if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
//Perform a background task to store the data
new SensorEventLoggerTask().execute(event);
}
}
//Get handles to files where data has to be stored
public void getFile(){
androidpath = Environment.getExternalStorageDirectory().toString();
try{
ps = new PrintStream(new FileOutputStream(androidpath + "/data_acc.dat"));
ps_bat = new PrintStream(new FileOutputStream(androidpath + "/data_bat.dat"));
ps_button = new PrintStream(new FileOutputStream(androidpath + "/data_button.dat"));
} catch (Exception e){
e.printStackTrace();
}
}
//Create receiver that listens to Intents from the Battery
private BroadcastReceiver battery_receiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent arg1) {
int bLevel = arg1.getIntExtra("level", -1); // gets the battery level
int bScale = arg1.getIntExtra("scale", -1);
ps_bat.println("" + System.currentTimeMillis() + ";" + bLevel + ";" + bScale);
}
};
#Override
public void onDestroy(){
if(wl.isHeld()) wl.release();
}
// For receiving the broadcast event of the buttons.
private BroadcastReceiver broadcastReceiver = new
BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("button") + ";" + intent.getStringExtra("time");
ps_button.println(msg);
}
};
//Create a Task that logs events to a file
private class SensorEventLoggerTask extends AsyncTask<SensorEvent, Void, Void>{
#Override
protected Void doInBackground(SensorEvent... events) {
//Getting the event and values
SensorEvent event = events[0];
String msg = "" + event.values[0] + ";" + event.values[1] + ";" + event.values[2];
//constructing the the line
msg = "" + System.currentTimeMillis() + ";" + msg;
//writing to file
ps.println(msg);
return null;
}
}
}
Manifest.xml
`<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wearabledemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-feature android:name="android.hardware.type.watch" />
<uses-sdk
android:minSdkVersion="20"
android:targetSdkVersion="20" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.DeviceDefault" >
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity
android:name=".WearableActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:enabled="true"
android:name=".WearableService"
android:label="#string/app_name"
android:exported="true"
android:permission="android.permission.WAKE_LOCK" >
</service>
</application>
</manifest>`

Categories