I want to send data from my Wear to the PhoneApp. I created a phone app with this AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sh.evolutio.car">
<uses-feature
android:name="android.software.leanback"
android:required="true" />
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<service android:name=".services.ListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<!-- <data android:scheme="wear" android:host="*" android:pathPrefix="/updatecar" /> -->
</intent-filter>
</service>
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
my ListenerService:
package sh.evolutio.car.services;
import android.content.Intent;
import android.util.Log;
import com.google.android.gms.wearable.MessageEvent;
import com.google.android.gms.wearable.WearableListenerService;
public class ListenerService extends WearableListenerService {
private static final String TAG = "ListenerService";
private static final String MESSAGE_PATH = "/updatecar";
#Override
public void onCreate() {
Log.d(TAG, "ListenerService created");
}
#Override
public void onMessageReceived(MessageEvent messageEvent) {
Log.d(TAG, "onMessageReceived");
if (messageEvent.getPath().equals(MESSAGE_PATH)) {
Log.d(TAG, "good message");
} else {
Log.d(TAG, "bad message");
}
}
}
my MainActivity with this onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
startService(new Intent(MainActivity.this, ListenerService.class));
}
When I start the App on my phone I got in the Logcat:
26131-26131/sh.evolutio.car D/ListenerService: ListenerService created
When I send with the wearapp some data to my phone, my ListenerService didn't fire the onMessageReceived method..
Here is my AndroidManifest from the wearapp:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sh.evolutio.carwear">
<uses-feature android:name="android.hardware.type.watch" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#android:style/Theme.DeviceDefault">
<uses-library
android:name="com.google.android.wearable"
android:required="true" />
<meta-data
android:name="com.google.android.wearable.standalone"
android:value="false" />
<activity
android:name=".MainActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The MainActivity from the wearapp looks like this:
package sh.evolutio.carwear;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.wearable.activity.WearableActivity;
import android.util.Log;
import android.view.View;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.wearable.MessageApi;
import com.google.android.gms.wearable.Node;
import com.google.android.gms.wearable.NodeApi;
import com.google.android.gms.wearable.Wearable;
public class MainActivity extends WearableActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static String TAG = "MainActivity";
private static final String MESSAGE_PATH = "/updatecar";
Node mNode; // the connected device to send the message to
GoogleApiClient mGoogleApiClient;
private boolean mResolvingError = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Connect the GoogleApiClient
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
sendMessage("test");
// Enables Always-on
setAmbientEnabled();
}
#Override
protected void onStart() {
super.onStart();
if (!mResolvingError) {
mGoogleApiClient.connect();
}
}
/**
* Resolve the node = the connected device to send the message to
*/
private void resolveNode() {
Log.d(TAG, "resolveNode");
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient)
.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult nodes) {
for (Node node : nodes.getNodes()) {
Log.d(TAG, "resolvedNode: " + node);
mNode = node;
}
}
});
}
#Override
public void onConnected(Bundle bundle) {
resolveNode();
}
#Override
public void onConnectionSuspended(int i) {}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d(TAG, "connectionResult: " + connectionResult);
}
/**
* Send message to mobile handheld
*/
private void sendMessage(String Key) {
if (mNode != null && mGoogleApiClient!= null && mGoogleApiClient.isConnected()) {
final String messageKey = Key;
Log.d(TAG, "isConnected: " + mGoogleApiClient.isConnected());
Log.d(TAG, "connected to: " + mNode.getId());
Task<Integer> sendTask = Wearable.getMessageClient(MainActivity.this).sendMessage(mNode.getId(), MESSAGE_PATH, messageKey.getBytes());
sendTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
#Override
public void onSuccess(Integer integer) {
Log.d(TAG, "onSuccess: " + integer);
}
});
sendTask.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: " + e.getMessage());
}
});
Wearable.MessageApi.sendMessage(mGoogleApiClient, mNode.getId(), MESSAGE_PATH, messageKey.getBytes()).setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(#NonNull MessageApi.SendMessageResult sendMessageResult) {
if (sendMessageResult.getStatus().isSuccess()) {
Log.v(TAG, "Message: { " + messageKey + " } sent to: " + mNode.getDisplayName());
} else {
// Log an error
Log.v(TAG, "ERROR: failed to send Message");
}
}
}
);
}
}
}
When I the message got send, i got this in the logcat from the wearapp:
sh.evolutio.carwear D/MainActivity: isConnected: true
sh.evolutio.carwear D/MainActivity: connected to: 778d0d53
sh.evolutio.carwear V/MainActivity: Message: { forward } sent to: HUAWEI Mate 10 Pro
sh.evolutio.carwear D/MainActivity: onSuccess: 17282
so the message was sent to my Mate 10 Pro. But why my Mate 10 Pro App can't receive the Message? Where is my mistake? I didn't find it.
In your mobile activity, do not start the service manually.
The service will be started automatically by Android on reception of the message.
And you need to uncomment your data pathPrefix definition in the AndroidManifest.
Related
I am developing an android app that requires a foreground service to sync data over bluetooth with computers. The foreground service works perfectly during the session where it is first run by the app. However, if I restart my phone, the service will not restart upon reboot, despite me returning START_STICKY within the onStartCommand function. I want it to start as soon as possible upon reboot just like my VPN does. How can I achieve this functionality?
Here is the code in question:
package com.example.app;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;
import static com.example.app.App.CONNECTED_DEVICES_CHANNEL_ID;
public class BluetoothSyncService extends Service {
private Utils utils;
private final BluetoothAdapter BLUETOOTH_ADAPTER = BluetoothAdapter.getDefaultAdapter();
private final String CONNECTED_PC_GROUP = "connectedPCS";
private final ArrayList<String> NOTIFIED_PC_ADDRESSES = new ArrayList<>();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
utils = Utils.getInstance(this);
NotificationCompat.Builder summaryNotificationBuilder =
new NotificationCompat.Builder(this,
CONNECTED_DEVICES_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_placeholder_logo)
.setContentTitle("Sync Service Background")
.setGroup(CONNECTED_PC_GROUP)
.setGroupSummary(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setOngoing(true);
int SUMMARY_NOTIFICATION_ID = 69;
startForeground(SUMMARY_NOTIFICATION_ID, summaryNotificationBuilder.build());
Thread serviceThread = new Thread(() -> {
while (true) {
handleConnectedDevices();
handleNotifications();
}
});
serviceThread.start();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void handleConnectedDevices() {
Set<BluetoothDevice> pairedDevices = BLUETOOTH_ADAPTER.getBondedDevices();
// Handle connected PCS
for (BluetoothDevice device : pairedDevices) {
if (isConnected(device.getAddress())) {
int deviceClass = device.getBluetoothClass().getDeviceClass();
if (deviceClass == BluetoothClass.Device.COMPUTER_LAPTOP |
deviceClass == BluetoothClass.Device.COMPUTER_DESKTOP) {
if (!utils.inPairedPCS(device.getAddress())) {
utils.addToPairedPCS(new PairedPC(device.getName(),
device.getAddress(), true));
} else {
if (utils.getPairedPCByAddress(device.getAddress()) != null) {
utils.getPairedPCByAddress(device.getAddress()).setConnected(true);
utils.savePairedPCSToDevice();
}
}
}
} else {
if (utils.inPairedPCS(device.getAddress())) {
if (utils.getPairedPCByAddress(device.getAddress()) != null) {
utils.getPairedPCByAddress(device.getAddress()).setConnected(false);
utils.savePairedPCSToDevice();
}
}
}
}
}
private void handleNotifications() {
NotificationManagerCompat notificationManager = NotificationManagerCompat
.from(this);
for (PairedPC pairedPC : utils.getPairedPCS()) {
int CONNECTION_NOTIFICATION_ID = 420;
if (pairedPC.isConnected()) {
if (pairedPC.isActive() && !NOTIFIED_PC_ADDRESSES.contains(pairedPC.getAddress())) {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
this, CONNECTED_DEVICES_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_pc)
.setContentTitle("Syncing PC")
.setContentText(pairedPC.getName())
.setGroup(CONNECTED_PC_GROUP)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true);
notificationManager.notify(pairedPC.getAddress(),
CONNECTION_NOTIFICATION_ID, notificationBuilder.build());
NOTIFIED_PC_ADDRESSES.add(pairedPC.getAddress());
} else if (!pairedPC.isActive()) {
notificationManager.cancel(pairedPC.getAddress(), CONNECTION_NOTIFICATION_ID);
NOTIFIED_PC_ADDRESSES.remove(pairedPC.getAddress());
}
} else {
if (NOTIFIED_PC_ADDRESSES.contains(pairedPC.getAddress())) {
notificationManager.cancel(pairedPC.getAddress(), CONNECTION_NOTIFICATION_ID);
NOTIFIED_PC_ADDRESSES.remove(pairedPC.getAddress());
}
}
}
}
private boolean isConnected(String address) {
Set<BluetoothDevice> pairedDevices = BLUETOOTH_ADAPTER.getBondedDevices();
for (BluetoothDevice device : pairedDevices) {
if (device.getAddress().equals(address)) {
Method method = null;
try {
method = device.getClass().getMethod("isConnected", (Class[]) null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
boolean connected = false;
try {
assert method != null;
Object methodInvocation = method.invoke(device, (Object[]) null);
if (methodInvocation != null) {
connected = (boolean) methodInvocation;
} else {
connected = false;
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return connected;
}
}
return false;
}
}
EDIT:
So I have tried using a broadcast receiver as suggested. Yet it is still not working. Here is the code for the receiver:
package com.example.app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Intent syncServiceIntent = new Intent(context, BluetoothSyncService.class);
context.startService(syncServiceIntent);
}
}
}
And here is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".App"
android:theme="#style/Theme.App">
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<activity android:name=".PermissionsActivity" />
<activity android:name=".MainActivity" />
<activity android:name=".StartupActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BluetoothSyncService"/>
</application>
</manifest>
EDIT 2:
SOLVED! Had to change context.startService(syncServiceIntent); to context.startForegroundService(syncServiceIntent);
I would like to figure out the cause of not registering a listener for a step counter sensor and how to overcome it.
MainActivity.java
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements SensorEventListener {
private SensorManager sensorManager;
private TextView count;
boolean activityRunning;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
count = (TextView) findViewById(R.id.counter);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
#Override
protected void onResume() {
super.onResume();
activityRunning = true;
Sensor countSensor = sensorManager
.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if (countSensor != null) {
if (sensorManager.registerListener(this, countSensor, SensorManager.SENSOR_DELAY_UI)) {
Toast.makeText(this, "registered", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "NOT registered", Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(this, "Count sensor not available", Toast.LENGTH_LONG).show();
}
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR)) {
Toast.makeText(getApplicationContext(), "STEP DETECTOR -> SUPPORTED", Toast.LENGTH_LONG).show();
Log.i("onResume", "step detector is supported");
} else {
Toast toast = Toast.makeText(getApplicationContext(), "STEP DETECTOR -> NO", Toast.LENGTH_LONG).show();
Log.i("onResume", "step detector is NOT supported");
}
}
#Override
protected void onPause() {
super.onPause();
activityRunning = false;
}
#Override
public void onSensorChanged(SensorEvent event) {
if (activityRunning) {
count.setText(String.valueOf(event.values[0]));
Toast.makeText(this, "onSensorChanged", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "changed, else branch", Toast.LENGTH_LONG).show();
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<uses-feature android:name="android.hardware.sensor.stepcounter" android:required="true"/>
<uses-feature android:name="android.hardware.SensorManager"/>
<uses-feature android:name="android.hardware.Sensor"/>
<uses-feature android:name="android.hardware.SensorEvent"/>
<uses-feature android:name="android.hardware.SensorEventListener"/>
<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>
</application>
</manifest>
It shows a Toast with NOT registered and this error in Logcat
E/SensorManager: registerListenerImpl sensorName:Step Counter,isWakeUpSensor:false
P.S. It pops up the STEP DETECTOR -> SUPPORTED toast for the check of the presence of this sensor. Is there something else that must be added to the manifest? Or probably, it is a wrong way of registering a listener?
Try adding the activity recognition permission to your manifest:
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
If you app targets Android 10+ (API 29 or later), you may need to request the permission at runtime as well:
https://developer.android.com/about/versions/10/privacy/changes#physical-activity-recognition
I am new to android stack. I am trying to start android service from the launcher activity. Service and Activity are defined in separate packages but it is not being started. In the logcat there is no exception or error. I have checked many questions on stackoverflow regarding this issue but that didn't worked. Below are the source code of my app. I have spent almost 8 hours on this issue. Any help would be great appreciation.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.test.app">
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".ui.LoginActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".messaging.AlertService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
AlertService.java:
package nl.test.app.messaging;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
public class AlertService extends Service {
public AlertService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(getApplicationContext(), "on create called\n", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
LoginActivity.java
package nl.test.app.ui;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.Toast;
import nl.test.app.R;
public class LoginActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
// function for service testing
public void onStartButtonClick(View view) {
Intent myIntentToStartAlertListActivity = new Intent();
String pkg = "nl.test.app.messaging";
String cls = "nl.test.app.messaging.AlertService";
myIntentToStartAlertListActivity.setComponent(new ComponentName(pkg, cls));
if (startService(myIntentToStartAlertListActivity) != null) {
Log.i("Service Started","Service started");
Toast.makeText(getApplicationContext(), "Service is running\n", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "Service is not running\n", Toast.LENGTH_LONG).show();
}
}
#Override
protected void onStop() {
super.onStop();
}
}
Try this
public void onStartButtonClick(View view) {
Intent myIntentToStartAlertListActivity = new Intent(LoginActivity.this, AlertService.class);
if (startService(myIntentToStartAlertListActivity) != null) {
Log.i("Service Started","Service started");
Toast.makeText(getApplicationContext(), "Service is running\n", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "Service is not running\n", Toast.LENGTH_LONG).show();
}
}
// function for service testing
public void onStartButtonClick(View view) {
Intent myIntentToStartAlertListActivity = new Intent(LoginActivity.this,AlertService.class);
String pkg = "nl.test.app.messaging";
String cls = "nl.test.app.messaging.AlertService";
myIntentToStartAlertListActivity.setComponent(new ComponentName(pkg, cls));
//startService(myIntentToStartAlertListActivity)
if (startService(myIntentToStartAlertListActivity) != null) {
Log.i("Service Started","Service started");
Toast.makeText(getApplicationContext(), "Service is running\n", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "Service is not running\n", Toast.LENGTH_LONG).show();
}
}
I am trying to modify and implement GCM (hmkode) in eclipse.I have imported project in eclipse and performed the necessary steps for setup.
http://hmkcode.com/android-google-cloud-messaging-tutorial/
After going through GCM sample on developer.google.com .The link says google includes GcmReceiver class by default.I removed old GcmBroadcastReceiver from hmkode sample and changed GcmMessageHandler to extend GcmListenerService instead of IntentService(in hmkode/original code).
Link:
https://developers.google.com/cloud-messaging/android/client
Problem:
When I try to send message to the client the client crashes with following exception in logcat
E/AndroidRuntime(20573): java.lang.RuntimeException: Unable to instantiate receiver com.google.android.gms.gcm.GcmReceiver: java.lang.ClassNotFoundException: Didn't find class "com.google.android.gms.gcm.GcmReceiver" on path: DexPathList[[zip file "/data/app/com.hmkcode.android.gcm-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
E/AndroidRuntime(20573):
at android.app.ActivityThread.handleReceiver(ActivityThread.java:2590)
My Class structure-
public class GcmMessageHandler extends GcmListenerService {
String mes;
private Handler handler;
public GcmMessageHandler() {
super();
}
//com.hmkcode.android.gcm.
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
handler = new Handler();
}
public void showToast(){
handler.post(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(),mes , Toast.LENGTH_LONG).show();
}
});
}
}
Activity class
public class MainActivity extends Activity implements OnClickListener {
Button btnRegId;
EditText etRegId;
GoogleCloudMessaging gcm;
String regid;
String PROJECT_NUMBER = "164502923904";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnRegId = (Button) findViewById(R.id.btnGetRegId);
etRegId = (EditText) findViewById(R.id.etRegId);
btnRegId.setOnClickListener(this);
}
public void getRegId(){
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
}
regid = gcm.register(PROJECT_NUMBER);
msg = "Device registered, registration ID=" + regid;
Log.i("GCM", msg);
} catch (IOException ex)
{
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
etRegId.setText(msg + "\n");
}
}.execute(null, null, null);
}
#Override
public void onClick(View v) {
getRegId();
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hmkcode.android.gcm"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.hmkcode.android.gcm.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="com.hmkcode.android.gcm.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
>
<activity
android:name="com.hmkcode.android.gcm.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.hmkcode.android.gcm" />
</intent-filter>
</receiver>
<service android:name="com.hmkcode.android.gcm.GcmMessageHandler" />
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
Also gcm.jar has been added as dependency and exported.
If you use Eclipse with regular android gcm jar, u need to add com.google.android.gcm.GcmReceiver instead of com.google.android.gcm.GCMBroadcastReceiver. This worked for me (at least temporarily)
my app crashes without any error message at googleApiClient.connect(). It never gets to onConnectionFailed. Here's what I have:
at MainActivity
public class MainMenu extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
public static final int REQUEST_LEADERBOARD = 1;
public static int SCREEN_WIDTH;
public static int SCREEN_HEIGHT;
private GoogleApiClient googleApiClient;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
SCREEN_WIDTH = metrics.widthPixels;
SCREEN_HEIGHT = metrics.heightPixels;
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Games.API).addScope(Games.SCOPE_GAMES)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
public void newGame(View view) {
Intent intent = new Intent(MainMenu.this, Game.class);
startActivity(intent);
finish();
}
public void highScore(View view) {
if (isSignedIn()) {
startActivityForResult(Games.Leaderboards.getLeaderboardIntent(googleApiClient,
String.valueOf(R.string.leaderboard_id)), REQUEST_LEADERBOARD);
}
else {
System.out.println("not sign in");
}
}
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
System.out.println("failed");
}
#Override
protected void onStart() {
super.onStart();
googleApiClient.connect(); //here it crashed
}
private boolean isSignedIn() {
return (googleApiClient != null && googleApiClient.isConnected());
}
}
What I've done:
I have generated signed APK, I have sha1 key. I published the app for alpha testing.
manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="my.package"
android:versionCode="2"
android:versionName="1.1">
<uses-sdk android:minSdkVersion="1"/>
<application android:icon="#drawable/ic_launcher" android:label="#string/app_name"
android:theme="#style/Theme.AppCompat.NoActionBar"
android:isGame="true" >
<meta-data
android:name="com.google.android.gms.games.APP_ID"
android:value="#string/app_id" />
<meta-data
android:name="my.package.version"
android:value="com.google.android.gms.version" />
<activity android:name=".activities.MainMenu">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".activities.Game"/>
<activity android:name=".activities.GameOver"/>
<activity android:name=".activities.HighScore"/>
</application>
</manifest>
I'm testing it on device with Google Play Service installed. Could you help me?
You should add the internet permission to your manifest.
<uses-permission android:name="android.permission.INTERNET"/>
If you need more help, you should show the error stacktrace.
Hope it helps you!
The solution is simple
<meta-data
android:name="my.package.version"
android:value="com.google.android.gms.version" />
is nonsense.
it should be:
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />