This question already has answers here:
Broadcast Receiver as inner class in Android
(6 answers)
Closed 7 years ago.
I'm trying to do a simple web view app based with push notifications. My app registers in background and do all the stuff, but when I send a push and my phone is supposed to show the push notification the app crashes. I've tried to debug and set a breakpoint in the onReceive() method, and I've also set breakpoints everywere but the app just crashes and don't stop anywere before doing it. I'm new in android studio and I don't understand at all the error messages so please if somebody can help me to find the error it would be greatly appreciated.
logCat:
05-04 14:41:46.349 30089-30089/com.example.alfredo.guiasandroid E/dalvikvm﹕ Could not find class 'android.app.Notification$Builder', referenced from method com.google.android.gms.common.GooglePlayServicesUtil.zza
05-04 14:41:46.349 30089-30089/com.example.alfredo.guiasandroid E/dalvikvm﹕ Could not find class 'android.app.AppOpsManager', referenced from method com.google.android.gms.common.GooglePlayServicesUtil.zza
05-04 14:43:34.879 10207-10207/? E/Launcher﹕ setWindowOpaque()
05-04 14:43:34.909 10207-10207/? E/Launcher﹕ MTP-LAUNCHER: media scanning not yet finished.
05-04 14:43:34.959 10207-10207/? E/Launcher﹕ setWindowOpaque()
05-04 14:43:44.879 30089-30089/com.example.alfredo.guiasandroid E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to instantiate receiver com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver: java.lang.InstantiationException: com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver
at android.app.ActivityThread.handleReceiver(ActivityThread.java:1777)
at android.app.ActivityThread.access$2400(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.InstantiationException: com.example.alfredo.guiasandroid.MainActivity$GcmBroadcastReceiver
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1409)
at android.app.ActivityThread.handleReceiver(ActivityThread.java:1768)
at android.app.ActivityThread.access$2400(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:985)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3687)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
at dalvik.system.NativeStart.main(Native Method)
05-04 14:43:44.979 10104-10118/? E/﹕ Dumpstate > /data/log/dumpstate_app_error
05-04 14:43:50.839 10207-10207/? E/Launcher﹕ setWindowOpaque()
mainfest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alfredo.guiasandroid" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="22"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<permission android:name="com.example.alfredo.guiasandroid.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.example.alfredo.guiasandroid.permission.C2D_MESSAGE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name">
<!-- Required for applications which use Google Play Services. -->
<!-- <meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" /> -->
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".MainActivity$GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<!--android:name=".MainActivity$GcmBroadcastReceiver" -->
<intent-filter>
<!--
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.example.alfredo.webapp" />
-->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.alfredo.guiasandroid" />
</intent-filter>
</receiver>
<!--
<service android:name=".MainActivity$GcmIntentService" />
-->
<service android:name=".MainActivity$GcmIntentService" />
</application>
</manifest>
<!--
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alfredo.guiasandroid" >
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<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>
-->
build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.alfredo.guiasandroid"
minSdkVersion 10
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.1.1'
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.google.android.gms:play-services:7.0.0'
}
mainActivity.java:
package com.example.alfredo.guiasandroid;
import android.os.Bundle;
import android.view.MenuItem;
import android.support.v4.app.NotificationCompat;
import android.app.Activity;
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.atomic.AtomicInteger;
public class MainActivity extends Activity {
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
String SENDER_ID = "example_Number";
/**
* Tag used on log messages.
*/
static final String TAG = "GCMDemo";
private WebView myWebView = null;
TextView mDisplay;
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
SharedPreferences prefs;
Context context;
String regid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "hola coño");
setContentView(R.layout.activity_main);
//initialize variables
context = getApplicationContext();
gcm = GoogleCloudMessaging.getInstance(this);
prefs = getPreferences(0);
mDisplay = new TextView(getApplicationContext());
// web view
this.myWebView = (WebView) findViewById(R.id.activity_main_webview);
myWebView.setWebViewClient(new WebViewClient());
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.loadUrl("http://mongini.net/guiasdelsur");
//remove shared prefs
/*
SharedPreferences prefs = getGCMPreferences(context);
SharedPreferences.Editor edit = prefs.edit();
edit.clear();
edit.commit();
*/
/*
SharedPreferences clear_cache = getSharedPreferences("registration_id", MODE_PRIVATE);
SharedPreferences.Editor edit = clear_cache.edit();
edit.clear();
edit.commit();
*/
// Check device for Play Services APK. If check succeeds, proceed with
// GCM registration.
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
Log.i(TAG,"ok");
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
// You need to do the Play Services APK check here too.
#Override
protected void onResume() {
super.onResume();
checkPlayServices();
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.d(TAG, "This device is not supported.");
finish();
}
return false;
}
return true;
}
/**
* Gets the current registration ID for application on GCM service.
* <p>
* If result is empty, the app needs to register.
*
* #return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing registration ID is not guaranteed to work with
// the new app version.
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
/**
* #return Application's {#code SharedPreferences}.
*/
private SharedPreferences getGCMPreferences(Context context) {
// This sample app persists the registration ID in shared preferences, but
// how you store the registration ID in your app is up to you.
return getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
/**
* Registers the application with GCM servers asynchronously.
* <p>
* Stores the registration ID and app versionCode in the application's
* shared preferences. com.example.alfredo.webapp.MainActivity
*/
private void registerInBackground() {
new AsyncTask<Void,Void,String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration id=" + regid;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
sendRegistrationIdToBackend();
// Save the regid for future use - no need to register again.
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regid);
editor.commit();
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
// Once registration is done, display the registration status
// string in the Activity's UI.
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute();
}
private String readStream(InputStream is) {
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int i = is.read();
while(i != -1) {
bo.write(i);
i = is.read();
}
return bo.toString();
} catch (IOException e) {
return "";
}
}
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
* or CCS to send messages to your app. Not needed for this demo since the
* device sends upstream messages to a server that echoes back the message
* using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
HttpURLConnection urlConnection = null;
try {
URL url = new URL("http://www.example.com/example.php?host=localhost&dbname=db_name&user=user&pass=pass&idPush="+regid);
urlConnection = (HttpURLConnection) url.openConnection();
/** Connecting to url */
urlConnection.connect();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
}catch(Exception e){
Log.d("Exception url ", e.toString());
}finally {
urlConnection.disconnect();
}
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
//aqui peta
private void storeRegistrationId(Context context, String regId) {
final SharedPreferences prefs = getGCMPreferences(context);
int appVersion = getAppVersion(context);
Log.i(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.commit();
}
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
public class GcmIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public GcmIntentService() {
super("GcmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
// The getMessageType() intent parameter must be the intent you received
// in your BroadcastReceiver.
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) { // has effect of unparcelling Bundle
/*
* Filter messages based on message type. Since it is likely that GCM
* will be extended in the future with new message types, just ignore
* any message types you're not interested in, or that you don't
* recognize.
*/
if (GoogleCloudMessaging.
MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server: " +
extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// This loop represents the service doing some work.
for (int i=0; i<5; i++) {
Log.i(TAG, "Working... " + (i+1)
+ "/5 # " + SystemClock.elapsedRealtime());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
Log.i(TAG, "Completed work # " + SystemClock.elapsedRealtime());
// Post notification of received message.
sendNotification("Received: " + extras.toString());
Log.i(TAG, "Received: " + extras.toString());
}
}
// Release the wake lock provided by the WakefulBroadcastReceiver.
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
// Put the message into a notification and post it.
// This is just one simple example of what you might choose to do with
// a GCM message.
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
//.setSmallIcon(R.drawable.ic_stat_gcm)
.setContentTitle("GCM Notification")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
//back device button
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && this.myWebView.canGoBack()) {
this.myWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
AFAIK the inner-class BroadcastReceivers only work if they are static.
In fact $ marks a static inner-class.
See https://stackoverflow.com/a/4392499/603270
Related
I am trying to get a phone number from the incoming call, and for Android, I've been trying to implement this answer, but I've been struggling because the answer is not full, has no example, and is very abstract, which makes it hard for a person without any knowledge about Platform Channel.
What I've tried:
I've implemented pigeon code in a ServiceReceiver, declared a variable that I want to update and pass to the flutter code, however when I receive a call, the app doesn't even ask for any permission, which makes me think that the onReceive() function is not called when the phone receives an incoming call.
Here is the code:
main.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './output.dart';
void main() {
runApp(MyApp());
}
// void onClick() async {
// SearchRequest request = SearchRequest()..query = 'test';
// Api api = Api();
// SearchReply reply = await api.search(request);
// print('reply: ${reply.result}');
// }
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static const platform = const MethodChannel('battery.ttsollution.com');
String _phoneNumber = '';
String _batteryLevel = 'Unknown battery level.';
void onClick() async {
SearchRequest request = SearchRequest()..query = 'test';
Api api = Api();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
Future<void> _getPhoneNumber() async {
String phoneNumber;
try {
final int result = await platform.invokeMethod('getPhoneNumber');
phoneNumber = "Phone number: $result";
} catch (e) {
phoneNumber = "Failed to get the phone number due to: $e";
}
setState(() {
_phoneNumber = phoneNumber;
});
}
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
child: Text('Get Phone Number'),
onPressed: onClick,
),
Text(_batteryLevel),
],
),
),
),
);
}
}
MainActivity.java:
package com.example.phone_application_java;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.telephony.TelephonyManager;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "battery.ttsollution.com";
#RequiresApi(api = VERSION_CODES.M)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Pigeon.Api.setup(getFlutterEngine().getDartExecutor().getBinaryMessenger(), new ServiceReceiver.MyApi());
}
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
// Note: this method is invoked on the main thread.
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
});
}
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).registerReceiver(null,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
}
ServiceReceiver.java:
package com.example.phone_application_java;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.os.Bundle;
import com.example.phone_application_java.Pigeon;
public class ServiceReceiver extends BroadcastReceiver {
static String phoneNumber = "Hiya";
static class MyApi implements Pigeon.Api {
#Override
public Pigeon.SearchReply search(Pigeon.SearchRequest arg) {
Pigeon.SearchReply reply = new Pigeon.SearchReply();
reply.setResult(ServiceReceiver.phoneNumber);
return reply;
}
}
#SuppressLint("UnsafeProtectedBroadcastReceiver")
#Override
public void onReceive(Context context, Intent intent) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
System.out.println("incomingNumber : " + incomingNumber);
phoneNumber = incomingNumber;
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.phone_application_java">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<application android:label="phone_application_java" android:icon="#mipmap/ic_launcher">
<receiver android:name=".ServiceReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="#style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="#style/NormalTheme" />
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="#drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
</manifest>
Flutter Doctor:
PS E:\Flutter Projects\phone_application_java> flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.6, on Microsoft Windows [Version 10.0.19042.928], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] Connected device (3 available)
• No issues found!
I understand this questions has been asked many times, for each question that has already been asked, the implementation is different from mine. The method I used create and call the intentService for a geocoder, was used from the android dev tutorial site.
https://developer.android.com/training/location/display-address.html.
I have gone over the the tutorial 3 times over to make sure I wasn't missing anything, but it is clear that I am.
Here is my manifest file, I have added the location and internet permissions.
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".selectRouteAndTransportMethod">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<service
android:name=".FetchAddressIntentService"
android:exported="false"/>
</activity>
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name="user.com.commuterapp.MapsActivity"
android:label="#string/title_activity_maps"></activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for
App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
Here is my main activity: All I am doing here is using a buttonClick to create the intent and to start the intent.
package user.com.commuterapp;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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.location.LocationServices;
import user.com.commuterapp.R;
import static java.lang.Boolean.TRUE;
public class selectRouteAndTransportMethod extends AppCompatActivity {
Intent mIntent;
PendingIntent mPendingIntent;
myLocation mCurrentLocation;
private AddressResultReceiver mResultReceiver;
public static final String TAG = selectRouteAndTransportMethod.class.getSimpleName();
/**
* ATTENTION: This was auto-generated to implement the App Indexing API.
* See https://g.co/AppIndexing/AndroidStudio for more information.
*/
private GoogleApiClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_route_and_transport_method);
mIntent = new Intent(this, selectRouteAndTransportMethod.class);
mPendingIntent = PendingIntent.getActivity(this, 0, mIntent, 0);
mCurrentLocation = new myLocation(this, this, mPendingIntent);
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
}
#Override
protected void onStart() {
super.onStart();
Log.d(TAG, "On Start");
mCurrentLocation.connect();
boolean connecting = mCurrentLocation.mGoogleApiClient.isConnecting();
boolean registered = mCurrentLocation.mGoogleApiClient.isConnectionCallbacksRegistered(mCurrentLocation);
//ConnectionResult connectionResult = mCurrentLocation.mGoogleApiClient.getConnectionResult(LocationServices.API);
if (connecting == TRUE) {
Log.d(TAG, "Connecting");
}
if (registered == TRUE) {
Log.d(TAG, "registered");
}
}
#Override
protected void onResume() {
super.onResume();
mCurrentLocation.connect();
}
#Override
protected void onPause() {
super.onPause();
// mCurrentLocation.disconnect();
}
protected void buttonOriginClick(View view)
{
Log.d(TAG,"retrieving address");
retrieveAddress();
}
protected void retrieveAddress() {
Intent geoCoderIntent = new Intent(this, FetchAddressIntentService.class);
geoCoderIntent.putExtra(FetchAddressIntentService.Constants.RECIEVER, mResultReceiver);
geoCoderIntent.putExtra(FetchAddressIntentService.Constants.LOCATION_DATA_EXTRA, mCurrentLocation.mCurrentLocation);
startService(geoCoderIntent);
}
}
And lastly here is the intentService class. This was straight out of the android dev tutorial site, nothing original here.
package user.com.commuterapp;
import android.app.IntentService;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.os.ResultReceiver;
import android.text.TextUtils;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static android.content.ContentValues.TAG;
/**
* Created by User on 1/21/2017.
*/
public class FetchAddressIntentService extends IntentService {
public final class Constants{
public static final int SUCCESS_RESULT = 0;
public static final int FAILURE_RESULT = 1;
public static final String PACKAGE_NAME =
"user.com.commuterapp";
public static final String RECIEVER = PACKAGE_NAME +
".RECIEVER";
public static final String RESULT_DATA_KEY = PACKAGE_NAME + ".RESULT_DATA_KEY";
public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
".LOCATION_DATA_EXTRA";
}
protected ResultReceiver mReceiver;
public FetchAddressIntentService()
{
super("FetchAddressIntentService");
}
private void deliverResultToReceiver(int resultCode, String message){
Bundle bundle = new Bundle();
bundle.putString(Constants.RESULT_DATA_KEY, message);
mReceiver.send(resultCode,bundle);
}
#Override
protected void onHandleIntent (Intent intent) {
Log.d(TAG, "GeoCoder Service Started");
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
String errorMessage = "";
Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA);
List<Address> addresses = null;
try {
addresses = geocoder.getFromLocation(
location.getLongitude(),
location.getLatitude(),
1);
}catch (IOException ioException) {
//catch network or other I/O Problems
errorMessage = getString(R.string.service_not_available);
Log.e(TAG, errorMessage);
} catch (IllegalArgumentException illegalArgumentException){
//catch invalid latitude or longitude values
errorMessage = getString(R.string.invalid_lat_long_used);
Log.e(TAG, errorMessage + "." + "Latitude =" + location.getLatitude()
+ ", Longitude = " + location.getLongitude(),
illegalArgumentException);
}
//handle cases where no addresses was foudn
if (addresses == null || addresses.size() == 0)
{
if(errorMessage.isEmpty()){
errorMessage = getString(R.string.no_address_found);
Log.e(TAG,errorMessage);
}
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
}else
{
Address address = addresses.get(0);
ArrayList<String> addressFragments = new ArrayList<String>();
//Fetch the address lines using getAddressLine
//join them and send them to the thread
for(int i = 0; i < address.getMaxAddressLineIndex(); i++){
addressFragments.add(address.getAddressLine(i));
}
Log.i(TAG, getString(R.string.address_found));
deliverResultToReceiver(Constants.SUCCESS_RESULT,
TextUtils.join(System.getProperty("line.seperator"), addressFragments));
}
}
}
EDIT: I have tried rewriting the geocoder following the instructions from the android dev site again, but no success.
EDIT2: I noticed in the manifest I declared the service, within the application declaration, instead of outside, I made the change but to no avail, any insights will be appreciated
I am writing an GCM support for Android 5.0.1. The app registers fine and sending a message to the server also seems ok, however the onReceive method on my BroadcastReceiver doesn't get fired.
But I am able to see my gsm message id in the log cat.
Here is the manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.freshmanapp.gcmtest">
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="8" />
<permission
android:name="com.freshmanapp.gcmtest.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.freshmanapp.gcmtest.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:theme="#style/AppTheme">
<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>
<meta-data
android:name="com.google.android.gms.version"
android:value="6" />
<receiver
android:name="com.freshmanapp.gcmtest.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.freshmanapp.gcmtest" />
</intent-filter>
</receiver>
<service android:name="com.freshmanapp.gcmtest.GcmIntentService" />
</manifest>
and here is my GcmBroadcastReceiver.java
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("GCM Reciever","Triggered");
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
MainActivity.java
package com.freshmanapp.gcmtest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Ramkumar on 16/04/15.
*/
public class MainActivity extends Activity {
private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
private static final String TAG = "GCMRelated";
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
String regid;
#Override
protected void onCreate(Bundle savedInstanceState) {
startService(new Intent(this, GcmIntentService.class));
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
regid = getRegistrationId(getApplicationContext());
Log.i("Registered Id ",regid);
if (regid.isEmpty()) {
new RegisterApp(getApplicationContext(), gcm, getAppVersion(getApplicationContext())).execute();
Toast.makeText(getApplicationContext(), "Device Registered Now", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(getApplicationContext(), "Device already Registered ("+regid+")", Toast.LENGTH_SHORT).show();
}
}
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean checkPlayServices() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i(TAG, "This device is not supported.");
finish();
}
return false;
}
return true;
}
/**
* Gets the current registration ID for application on GCM service.
* <p>
* If result is empty, the app needs to register.
*
* #return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(getApplicationContext());
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
/**
* #return Application's {#code SharedPreferences}.
*/
private SharedPreferences getGCMPreferences(Context context) {
// This sample app persists the registration ID in shared preferences, but
// how you store the regID in your app is up to you.
return getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
}
Are you calling registerReceiver in your Activity?
You will need to call something like registerReceiver(new GcmBroadcastReceiver(), new IntentFilter(DISPLAY_MESSAGE_ACTION));
I am currently working with ActivityRecognitionClient , but unfortunately that Google has announced that the class had been deprecated and to use GoogleApiClient instead.
Not sure if I am doing it wrong or not, I am getting confused with the new API file. I have imported the Google Play Libraries, setup the API v2 key. I followed an online source on coding up the ActivityRecognitionClient version.
Below are the codes of the different files, whenever I switch tab to the actRecog it crashes and points the error to this line with a null pointer exception.
mActivityRecognitionPendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Full source code below : (API v2 keys are intentionally hidden for privacy purposes.)
MainActivity.java
package com.example.healthgps;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.app.Activity;
import android.app.TabActivity;
import android.content.Context;
import android.content.Intent;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
public class MainActivity extends TabActivity {
TabHost mTabHost;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTabHost = getTabHost();
TabSpec firstSpec = mTabHost.newTabSpec("Stats");
firstSpec.setIndicator("Stats");
Intent firstIntent = new Intent(this, FirstActivity.class);
firstSpec.setContent(firstIntent);
TabSpec thirdSpec = mTabHost.newTabSpec("ActRecog");
thirdSpec.setIndicator("ActRecog");
Intent thirdIntent = new Intent(this, activityrecignition.class);
thirdSpec.setContent(thirdIntent);
mTabHost.addTab(firstSpec);
mTabHost.addTab(thirdSpec);
}
}
Manifest XML File :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.healthgps"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="17" />
<permission
android:name="com.example.healthgps.permission.MAPS_RECEIVE"
android:protectionLevel="signature" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="com.example.healthgps.permission.MAPS_RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" />
<activity
android:name="com.example.healthgps.MainActivity"
android:label="Health Kit"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".FirstActivity"
android:label="Health Kit" >
</activity>
<activity
android:name=".activityrecignition"
android:label="Health Kit" >
</activity>
<service
android:name="com.example.healthgps.ActivityRecognitionIntentService"
android:label="#string/app_name"
android:exported="false">
</service>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
activityrecignition.java (purposely typo for the name)
package com.example.healthgps;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
public class activityrecignition extends FragmentActivity implements ConnectionCallbacks,OnConnectionFailedListener {
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 20;
public static final int DETECTION_INTERVAL_MILLISECONDS =
MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private GoogleApiClient mGoogleApiClient;
private Context mContext;
private Intent intent;
TextView tv;
ActivityRecognitionIntentService ar;
private boolean mInProgress;
public enum REQUEST_TYPE {START, STOP}
private REQUEST_TYPE mRequestType;
Intent i;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.third);
mActivityRecognitionPendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
tv=(TextView) findViewById(R.id.activityname);
mInProgress=false;
ar.onHandleIntent(i);
}
#SuppressLint("NewApi") public static class ErrorDialogFragment extends DialogFragment {
private Dialog mDialog;
#SuppressLint("NewApi") public ErrorDialogFragment() {
super();
mDialog = null;
}
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
public void show(FragmentManager supportFragmentManager, String tag) {
}
}
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
switch (resultCode) {
case Activity.RESULT_OK :
break;
}
}
}
private boolean servicesConnected() {
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == resultCode) {
Log.d("Activity Recognition", "Google Play services is available.");
return true;
} else {
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
resultCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
return false;
}
}
public class ActivityRecognitionIntentService extends IntentService {
public ActivityRecognitionIntentService(String name) {
super(name);
// TODO Auto-generated constructor stub
}
private String getNameFromType(int activityType) {
switch(activityType) {
case DetectedActivity.IN_VEHICLE:
return "in_vehicle";
case DetectedActivity.ON_BICYCLE:
return "on_bicycle";
case DetectedActivity.ON_FOOT:
return "on_foot";
case DetectedActivity.STILL:
return "still";
case DetectedActivity.UNKNOWN:
return "unknown";
case DetectedActivity.TILTING:
return "tilting";
}
return "unknown";
}
#Override
protected void onHandleIntent(Intent intent) {
// If the incoming intent contains an update
if (ActivityRecognitionResult.hasResult(intent)) {
// Get the update
ActivityRecognitionResult result =
ActivityRecognitionResult.extractResult(intent);
// Get the most probable activity
DetectedActivity mostProbableActivity =
result.getMostProbableActivity();
/*
* Get the probability that this activity is the
* the user's actual activity
*/
int confidence = mostProbableActivity.getConfidence();
/*
* Get an integer describing the type of activity
*/
int activityType = mostProbableActivity.getType();
String activityName = getNameFromType(activityType);
tv.setText(activityName);
/*
* At this point, you have retrieved all the information
* for the current update. You can display this
* information to the user in a notification, or
* send it to an Activity or Service in a broadcast
* Intent.
*/
} else {
/*
* This implementation ignores intents that don't contain
* an activity update. If you wish, you can report them as
* errors.
*/
tv.setText("There are no updates!!!");
}
}
}
public void onClick(View v){
if(v.getId()==R.id.Start){
startUpdates();
}
if(v.getId()==R.id.Stop){
stopUpdates();
}
}
public void startUpdates() {
// Check for Google Play services
mRequestType = REQUEST_TYPE.START;
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mGoogleApiClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
mInProgress = true;
mGoogleApiClient.disconnect();
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
}
}
#Override
public void onConnected(Bundle dataBundle) {
// TODO Auto-generated method stub
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
/*
* Since the preceding call is synchronous, turn off the
* in progress flag and disconnect the client
*/
mInProgress = false;
mGoogleApiClient.disconnect();
switch (mRequestType) {
case START :
/*
* Request activity recognition updates using the
* preset detection interval and PendingIntent.
* This call is synchronous.
*/
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, DETECTION_INTERVAL_MILLISECONDS, mActivityRecognitionPendingIntent);
break;
case STOP :
ActivityRecognition.ActivityRecognitionApi.removeActivityUpdates(mGoogleApiClient, mActivityRecognitionPendingIntent);
/*
* An enum was added to the definition of REQUEST_TYPE,
* but it doesn't match a known case. Throw an exception.
*/
default :
try {
throw new Exception("Unknown request type in onConnected().");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
mInProgress = false;
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Turn off the request flag
mInProgress = false;
/*
* If the error has a resolution, start a Google Play services
* activity to resolve it.
*/
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (SendIntentException e) {
// Log the error
e.printStackTrace();
}
// If no resolution is available, display an error dialog
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
}
}
public void stopUpdates() {
// Set the request type to STOP
mRequestType = REQUEST_TYPE.STOP;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn't present, the request can be
* restarted.
*/
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mGoogleApiClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
}
}
In the old code, it contains a part where the ActivityRecognitionClient requires an instantiation, but GoogleApiClient doesn't have.
Is there anyone who manage to switch over to the new API already ? I need some guide to get it there.
Thanks.
you must use GoogleApiClient.Builder in the following way
GoogleApiClient.Builder builder = new GoogleApiClient.Builder(<context>)
.addApi(<some api, i.e LocationServices.API>)
.addConnectionCallbacks(new ConnectionCallbacks() {
#Override
public void onConnectionSuspended(int arg) {}
#Override
public void onConnected(Bundle arg0) {
Intent intent = new Intent(getApplicationContext(), ActivityRecognitionService.class); // your custom ARS class
mPendingIntent = PendingIntent.getService(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi
.requestActivityUpdates(mGoogleApiClient, ACTIVITY_RECOGNITION_INTERVAL, mPendingIntent);}
}
.addOnConnectionFailedListener(new OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult arg0) {
}
});
mGoogleApiClient = builder.build();
mGoogleApiClient.connect();
I am trying to implement Stephen Wylie's Google Drive example (here). Here is my code:
package com.googledrive.googledriveapp;
// For Google Drive / Play Services
// Version 1.1 - Added new comments & removed dead code
// Stephen Wylie - 10/20/2012
import java.io.IOException;
import java.util.ArrayList;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.common.AccountPicker;
import com.google.api.client.auth.oauth2.BearerToken;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.android2.AndroidHttp;
import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.json.JsonHttpRequest;
import com.google.api.client.http.json.JsonHttpRequestInitializer;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Apps.List;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.DriveRequest;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
public class MainActivity extends Activity {
private static final int CHOOSE_ACCOUNT=0;
private static String accountName;
private static int REQUEST_TOKEN=0;
private Button btn_drive;
private Context ctx = this;
private Activity a = this;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set up the GUI layout
setContentView(R.layout.activity_main);
// set the variables to access the GUI controls
btn_drive = (Button) findViewById(R.id.btn_drive);
btn_drive.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
chooseAccount();
}
});
}
public void chooseAccount() {
Intent intent = AccountPicker.newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null, null, null, null);
startActivityForResult(intent, CHOOSE_ACCOUNT);
}
// Fetch the access token asynchronously.
void getAndUseAuthTokenInAsyncTask(Account account) {
AsyncTask<Account, String, String> task = new AsyncTask<Account, String, String>() {
ProgressDialog progressDlg;
AsyncTask<Account, String, String> me = this;
#Override
protected void onPreExecute() {
progressDlg = new ProgressDialog(ctx, ProgressDialog.STYLE_SPINNER);
progressDlg.setMax(100);
progressDlg.setTitle("Validating...");
progressDlg.setMessage("Verifying the login data you entered...\n\nThis action will time out after 10 seconds.");
progressDlg.setCancelable(false);
progressDlg.setIndeterminate(false);
progressDlg.setOnCancelListener(new android.content.DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface d) {
progressDlg.dismiss();
me.cancel(true);
}
});
progressDlg.show();
}
#Override
protected String doInBackground(Account... params) {
return getAccessToken(params[0]);
}
#Override
protected void onPostExecute(String s) {
if (s == null) {
// Wait for the extra intent
} else {
accountName = s;
getDriveFiles();
}
progressDlg.dismiss();
}
};
task.execute(account);
}
/**
* Fetches the token from a particular Google account chosen by the user. DO NOT RUN THIS DIRECTLY. It must be run asynchronously inside an AsyncTask.
* #param activity
* #param account
* #return
*/
private String getAccessToken(Account account) {
try {
return GoogleAuthUtil.getToken(ctx, account.name, "oauth2:" + DriveScopes.DRIVE_READONLY); // IMPORTANT: DriveScopes must be changed depending on what level of access you want
} catch (UserRecoverableAuthException e) {
// Start the Approval Screen intent, if not run from an Activity, add the Intent.FLAG_ACTIVITY_NEW_TASK flag.
a.startActivityForResult(e.getIntent(), REQUEST_TOKEN);
e.printStackTrace();
return null;
} catch (GoogleAuthException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private Drive getDriveService() {
HttpTransport ht = AndroidHttp.newCompatibleTransport(); // Makes a transport compatible with both Android 2.2- and 2.3+
JacksonFactory jf = new JacksonFactory(); // You need a JSON parser to help you out with the API response
Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accountName);
HttpRequestFactory rf = ht.createRequestFactory(credential);
Drive.Builder b = new Drive.Builder(ht, jf, null);
b.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
#Override
public void initialize(JsonHttpRequest request) throws IOException {
DriveRequest driveRequest = (DriveRequest) request;
driveRequest.setPrettyPrint(true);
driveRequest.setOauthToken(accountName);
}
});
return b.build();
}
/**
* Obtains a list of all files on the signed-in user's Google Drive account.
*/
private void getDriveFiles() {
Drive service = getDriveService();
Log.d("SiteTrack", "FUNCTION getDriveFiles()");
Files.List request;
try {
request = service.files().list(); // .setQ("mimeType=\"text/plain\"");
} catch (IOException e) {
e.printStackTrace();
return;
}
do {
FileList files;
try {
Log.d("SiteTrack", request.toString());
files = request.execute();
} catch (IOException e) {
e.printStackTrace();
Log.d("SiteTrack", "Exception");
return;
}
ArrayList<File> fileList = (ArrayList<File>) files.getItems();
Log.d("SiteTrack", "Files found: " + files.getItems().size());
for (File f : fileList) {
String fileId = f.getId();
String title = f.getTitle();
Log.d("SiteTrack", "File " + fileId + ": " + title);
}
request.setPageToken(files.getNextPageToken());
} while (request.getPageToken() != null && request.getPageToken().length() >= 0);
}
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == CHOOSE_ACCOUNT && resultCode == RESULT_OK) {
accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
GoogleAccountManager gam = new GoogleAccountManager(this);
getAndUseAuthTokenInAsyncTask(gam.getAccountByName(accountName));
Log.d("SiteTrack", "CHOOSE_ACCOUNT");
} else if (requestCode == REQUEST_TOKEN && resultCode == RESULT_OK) {
accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
Log.d("SiteTrack", "REQUEST_TOKEN");
}
}
}
Here is my manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.googledrive.googledriveapp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="android.app.ActivityGroup" />
</activity>
</application>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
Here is my activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/btn_drive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Connect to Google Drive" />
</LinearLayout>
And here is the LogCat error I receive. It occurs when the button is pressed:
10-28 00:25:28.637: E/AndroidRuntime(842): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.google.android.gms.common.account.CHOOSE_ACCOUNT (has extras) }
10-28 00:25:28.637: E/AndroidRuntime(842): at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1512)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1384)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.app.Activity.startActivityForResult(Activity.java:3190)
10-28 00:25:28.637: E/AndroidRuntime(842): at com.googledrive.googledriveapp.MainActivity.chooseAccount(MainActivity.java:67)
10-28 00:25:28.637: E/AndroidRuntime(842): at com.googledrive.googledriveapp.MainActivity$1.onClick(MainActivity.java:60)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.view.View.performClick(View.java:3511)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.view.View$PerformClick.run(View.java:14105)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.os.Handler.handleCallback(Handler.java:605)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.os.Handler.dispatchMessage(Handler.java:92)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.os.Looper.loop(Looper.java:137)
10-28 00:25:28.637: E/AndroidRuntime(842): at android.app.ActivityThread.main(ActivityThread.java:4424)
10-28 00:25:28.637: E/AndroidRuntime(842): at java.lang.reflect.Method.invokeNative(Native Method)
10-28 00:25:28.637: E/AndroidRuntime(842): at java.lang.reflect.Method.invoke(Method.java:511)
10-28 00:25:28.637: E/AndroidRuntime(842): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
10-28 00:25:28.637: E/AndroidRuntime(842): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
10-28 00:25:28.637: E/AndroidRuntime(842): at dalvik.system.NativeStart.main(Native Method)
Can anyone help out?
We've been doing some experimenting, and our current theory is that the new Google OAuth Library depends on having the latest version of Google Play.
We found that if your device still has the Android Marketplace, or an older Google Play we couldn't get OAuth to work.
So you might try opening the Android Marketplace or Google Play App to kick off an upgrade.
Open the Android Marketplace, Accept the Upgrade to Google Play.
Close the Marketplace, and open the Google Play App.
Accept the Terms of Service for Google Play.
Wait a few seconds, sacrifice a chicken, and then you should be able to run Google OAuth.
EDIT: Looks like Google Provides some guidance on what your app should do if your users are missing the correct Google Play version. See: https://developer.android.com/google/play-services/setup.html#ensure
Google Drive API seems (acording to the Google people) to only works on a real Device, in the Emulator it will crash with this error.
So my Advice, try on a real device.