My app crashes at the very begining, in the first line of code of the protected void onCreate(Bundle savedInstanceState) method, the line is super.onCreate(savedInstanceState);, and when I debugged it has pointed me to a file (ActivityThread.java) full of errors. There is a lot of import statement not working and lot of unresolved symbols. I never had problems with this file and I wonder if I have changed something important in manifest or build.graddle, because I'm really stuck in android studio and I'm following google tutorials but I've also heard they are deprecated so I'm also following this code. Besides, I've updated today the android studio software and my project weren't there when I've opened, so I had to import it again and I don't know if it can be the cause of the problem. I haven't posted the logcat because it's allmost infinite, but I'll do an edit if necesary. Any help would be appreciated.
This is a screenshot to (ActivityThread.java)
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<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" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="com.example.alfredo.webapp.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.example.alfredo.webapp.permission.C2D_MESSAGE"/>
<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.google.android.gcm.demo.app" />
</intent-filter>
</receiver>
<!--
<service android:name=".MainActivity$GcmIntentService" />
-->
<service android:name=".MainActivity$GcmIntentService" />
</application>
build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.alfredo.webapp"
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.0.0'
compile 'com.google.android.gms:play-services:7.0.0'
}
And main activity:
package com.example.alfredo.webapp;
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.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
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 ActionBarActivity {
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 = "46075xxxx056";
/**
* 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.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=xxxx&dbname=xxx&user=mongini_webapp&pass=xxx&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);
}
}
Edit to add logcat (added a very small portion because it's almost infinite):
05-04 08:01:33.850 23385-23454/? E/dalvikvm﹕ Could not find class 'android.app.AppOpsManager', referenced from method com.google.android.gms.common.ff.a
05-04 08:01:33.880 23385-23454/? E/dalvikvm﹕ Could not find class 'android.app.Notification$Builder', referenced from method com.google.android.gms.common.ff.b
05-04 08:01:36.010 10233-23463/? E/TalkProvider﹕ replaceContactWithContactId: contactId==0!!! pablogarces123#gmail.com, acct=1
05-04 08:01:36.010 10233-23463/? E/TalkProvider﹕ insert presence failed for account=1 username=pablogarces123#gmail.com client_type=2 status= cap=0 priority=0 mode=3
05-04 08:01:36.090 10233-23463/? E/TalkProvider﹕ replaceContactWithContactId: contactId==0!!! pablogarces123#gmail.com, acct=1
05-04 08:01:36.090 10233-23463/? E/TalkProvider﹕ insert presence failed for account=1 username=pablogarces123#gmail.com client_type=2 status= cap=0 priority=0 mode=3
05-04 08:01:37.469 23385-23466/? E/dalvikvm﹕ Could not find class 'android.os.UserManager', referenced from method com.google.android.gms.common.util.a.b
05-04 08:01:37.469 23385-23466/? E/dalvikvm﹕ Could not find class 'android.os.UserManager', referenced from method com.google.android.gms.common.util.a.c
05-04 08:01:37.469 23385-23466/? E/dalvikvm﹕ Could not find class 'android.os.UserManager', referenced from method com.google.android.gms.common.util.a.e
05-04 08:01:37.499 23223-23241/? E/﹕ statfs /mnt/secure/asec failed, errno: 13
05-04 08:01:40.499 23223-23233/? E/﹕ statfs /mnt/secure/asec failed, errno: 13
05-04 08:01:49.679 10207-10207/? E/Launcher﹕ setWindowOpaque()
05-04 08:01:49.729 10207-10207/? E/Launcher﹕ MTP-LAUNCHER: media scanning not yet finished.
05-04 08:01:57.119 10079-23504/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:01:57.159 10079-23506/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:01:57.179 10079-23508/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:01:57.199 10079-23510/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:01:57.219 10079-23512/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:01:57.239 10079-23514/? E/PlayerDriver﹕ Creating Non-Tunnel mode playback - uncompressed MIO
05-04 08:02:04.759 23530-23530/? E/dalvikvm﹕ Could not find class 'android.database.sqlite.SQLiteCantOpenDatabaseException', referenced from method com.google.android.gms.plus.provider.PlusProvider.a
05-04 08:02:15.619 10233-10233/? E/AuthorizationBluetoothService﹕ Proximity feature is not enabled.
05-04 08:02:16.169 23602-23602/? E/dalvikvm﹕ Could not find class 'android.database.sqlite.SQLiteCantOpenDatabaseException', referenced from method com.google.android.gms.plus.provider.PlusProvider.a
05-04 08:02:17.359 10233-10233/? E/AuthorizationBluetoothService﹕ Proximity feature is not enabled.
05-04 08:02:25.849 23551-23551/? E/Finsky﹕ [1] InstallerTask.requireInternalStorageOrCancel: Cancel download of com.google.android.gms because insufficient free space
05-04 08:03:25.599 23641-23641/? E/dalvikvm﹕ Could not find class 'android.database.sqlite.SQLiteCantOpenDatabaseException', referenced from method com.google.android.gms.plus.provider.PlusProvider.a
05-04 08:03:26.139 10233-10233/? E/AuthorizationBluetoothService﹕ Proximity feature is not enabled.
05-04 08:03:26.759 23641-23656/? E/dalvikvm﹕ Could not find class 'android.app.AppOpsManager', referenced from method com.google.android.gms.common.ff.a
05-04 08:03:26.759 23641-23656/? E/dalvikvm﹕ Could not find class 'android.app.Notification$Builder', referenced from method com.google.android.gms.common.ff.b
05-04 08:03:27.959 23641-23656/? E/dalvikvm﹕ Could not find class 'android.security.KeyPairGeneratorSpec$Builder', referenced from method com.google.android.gms.auth.e.a.a
05-04 08:03:30.809 23641-23664/? E/dalvikvm﹕ Could not find class 'android.os.UserManager', referenced from method com.google.android.gms.common.util.a.b
I've finally started a new project from scratch, copied the code and figured it was because I were extending actionBarActivity (deprecated) instead of Activity, and it looks like this class need a theme and I weren't using one. Thank you very much to all who has answered or read my question.
Related
I am currently developing an Android App, which integrates Google Calendar in it. As we know if we wanted to choose an activity to be the starting activity, we just add the "intent-filter" thingy. I added at my Landing Page activity but when I debug on Emulator, it will auto add the "intent-filter" at my Google Calendar activity part and comment out the one I added at my Landing Page.
This is the code at AndroidManifest.xml
<activity
android:name="com.example.dylicious.mydoctors.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>
<activity
android:name="com.example.dylicious.mydoctors.ViewDocActivity"
android:label="#string/title_activity_view_doc" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.ViewDoctorProf"
android:label="#string/title_activity_view_doctor_prof" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.StorePatientProfile"
android:label="#string/title_activity_store_patient_profile" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.ViewPatientProfile"
android:label="#string/title_activity_view_patient_profile" >
</activity>
<activity
android:name="com.example.dylicious.mydoctors.AppointmentActivity"
android:label="#string/title_activity_appointment" >
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN" />-->
<!--<category android:name="android.intent.category.LAUNCHER" />-->
<!--</intent-filter>-->
</activity>
This is the one I edited, AppointmentActivity is the Google Calendar part. I kept commenting out and delete the part in AppointmentActivity and clean solution and etc. but it doesn't work anything. Just want to check if I had miss out anything as I'm just a newbie in the Android App Development field. Thanks a bunch in advance!
While this is the class for AppointmentActivity.java
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.CalendarScopes;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.Arrays;
import java.util.List;
public class AppointmentActivity extends Activity {
com.google.api.services.calendar.Calendar mService;
GoogleAccountCredential credential;
private TextView mStatusText;
private TextView mResultsText;
ProgressDialog mProgress;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = { CalendarScopes.CALENDAR_READONLY };
/**
* Create the main activity.
* #param savedInstanceState previously saved instance data.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout activityLayout = new LinearLayout(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
activityLayout.setLayoutParams(lp);
activityLayout.setOrientation(LinearLayout.VERTICAL);
activityLayout.setPadding(16, 16, 16, 16);
ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
mStatusText = new TextView(this);
mStatusText.setLayoutParams(tlp);
mStatusText.setTypeface(null, Typeface.BOLD);
mStatusText.setText("Retrieving data...");
activityLayout.addView(mStatusText);
mResultsText = new TextView(this);
mResultsText.setLayoutParams(tlp);
mResultsText.setPadding(16, 16, 16, 16);
mResultsText.setVerticalScrollBarEnabled(true);
mResultsText.setMovementMethod(new ScrollingMovementMethod());
activityLayout.addView(mResultsText);
mProgress = new ProgressDialog(this);
mProgress.setMessage("Calling Google Calendar API ...");
setContentView(activityLayout);
// Initialize credentials and service object.
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
credential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff())
.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
mService = new com.google.api.services.calendar.Calendar.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Calendar API Android Quickstart")
.build();
}
/**
* Called whenever this activity is pushed to the foreground, such as after
* a call to onCreate().
*/
#Override
protected void onResume() {
super.onResume();
if (isGooglePlayServicesAvailable()) {
refreshResults();
} else {
mStatusText.setText("Google Play Services required: " +
"after installing, close and relaunch this app.");
}
}
/**
* Called when an activity launched here (specifically, AccountPicker
* and authorization) exits, giving you the requestCode you started it with,
* the resultCode it returned, and any additional data from it.
* #param requestCode code indicating which activity result is incoming.
* #param resultCode code indicating the result of the incoming
* activity result.
* #param data Intent (containing result data) returned by incoming
* activity result.
*/
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
isGooglePlayServicesAvailable();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
credential.setSelectedAccountName(accountName);
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.commit();
}
} else if (resultCode == RESULT_CANCELED) {
mStatusText.setText("Account unspecified.");
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode != RESULT_OK) {
chooseAccount();
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Attempt to get a set of data from the Google Calendar API to display. If the
* email address isn't known yet, then call chooseAccount() method so the
* user can pick an account.
*/
private void refreshResults() {
if (credential.getSelectedAccountName() == null) {
chooseAccount();
} else {
if (isDeviceOnline()) {
mProgress.show();
new ApiAsyncTask(this).execute();
} else {
mStatusText.setText("No network connection available.");
}
}
}
/**
* Clear any existing Google Calendar API data from the TextView and update
* the header message; called from background threads and async tasks
* that need to update the UI (in the UI thread).
*/
public void clearResultsText() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mStatusText.setText("Retrieving data…");
mResultsText.setText("");
}
});
}
/**
* Fill the data TextView with the given List of Strings; called from
* background threads and async tasks that need to update the UI (in the
* UI thread).
* #param dataStrings a List of Strings to populate the main TextView with.
*/
public void updateResultsText(final List<String> dataStrings) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (dataStrings == null) {
mStatusText.setText("Error retrieving data!");
} else if (dataStrings.size() == 0) {
mStatusText.setText("No data found.");
} else {
mStatusText.setText("Data retrieved using" +
" the Google Calendar API:");
mResultsText.setText(TextUtils.join("\n\n", dataStrings));
}
}
});
}
/**
* Show a status message in the list header TextView; called from background
* threads and async tasks that need to update the UI (in the UI thread).
* #param message a String to display in the UI header TextView.
*/
public void updateStatus(final String message) {
runOnUiThread(new Runnable() {
#Override
public void run() {
mStatusText.setText(message);
}
});
}
/**
* Starts an activity in Google Play Services so the user can pick an
* account.
*/
private void chooseAccount() {
startActivityForResult(
credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
/**
* Checks whether the device currently has a network connection.
* #return true if the device has a network connection, false otherwise.
*/
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Check that Google Play services APK is installed and up to date. Will
* launch an error dialog for the user to update Google Play Services if
* possible.
* #return true if Google Play Services is available and up to
* date on this device; false otherwise.
*/
private boolean isGooglePlayServicesAvailable() {
final int connectionStatusCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (GooglePlayServicesUtil.isUserRecoverableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
return false;
} else if (connectionStatusCode != ConnectionResult.SUCCESS ) {
return false;
}
return true;
}
/**
* Display an error dialog showing that Google Play Services is missing
* or out of date.
* #param connectionStatusCode code describing the presence (or lack of)
* Google Play Services on this device.
*/
void showGooglePlayServicesAvailabilityErrorDialog(
final int connectionStatusCode) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
connectionStatusCode,
AppointmentActivity.this,
REQUEST_GOOGLE_PLAY_SERVICES);
dialog.show();
}
});
}
}
Thanks!!
omit the intent-filter in your AppointmentActivity AndroidManifest file declaration. since calling action android:name="android.intent.action.MAIN" which defines the main entry point in your application and android:name="android.intent.category.LAUNCHER" means that the entry point should be listed in the application launcher, so you see there can't be 2 main entry points in the application.
I'm trying to make a web app with push notifications. I'm using server key with no ip adress filtering so any ip is allowed (when I was adding my public shared ip from my server I was getting a 404 forbiden error...)
Now it looks like it works, when I send a message I get this :
{"multicast_id":768704xxxxx3856470,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1430054xxxxxx71%60cd63d3f9fd7ecd"}]}{"registration_ids":["APAxxxea-Q0s5pbFZHpSu4BfKdabxxxxxxxxWjPGItVSc54U6_t7WsJme5Z5UjYldhry66rTHk95CUcRR7m2iAtfvPgTklbWlHn4YiDZi1Qxxxxxxxg9iz5jKMPiulvIQTTM5F16THVjw"],"data":{"message":"New content available!"}}
But I never receive a push in my device. I've been following tutorials form google and also form StackOverflow answers, but I'm still very noob in android and I'm sure there is something wrong in my code and I can't figure out what it is, any help would be greatly appreciated.
main activity:
package com.example.alfredo.webapp;
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.Bundle;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
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 ActionBarActivity {
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 = "xxxxxxxxxxxx";
/**
* 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.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.mongini.net/android.php?host=localhost&dbname=bdapp&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);
}
}
Manifest file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alfredo.webapp" >
<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.example.alfredo.webapp.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.example.alfredo.webapp.permission.C2D_MESSAGE"/>
<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" >
<receiver
android:name=".MainActivity$GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
</receiver>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.example.alfredo.webapp" />
</intent-filter>
</activity>
<service android:name=".MainActivity$GcmIntentService" />
</application>
</manifest>
build gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.example.alfredo.webapp"
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.0.0'
compile 'com.google.android.gms:play-services:7.0.0'
}
It will be hard to find the real solution without fixing your BroadcastReceiver implementation first.
It looks like you are following a deprecated sample. In the manifest, you no longer need the com.google.android.c2dm.intent.REGISTRATION action. Also, your receiver tag/entry should be outside the activity tag, and it should have an intent filter (your intent filter is outside the receiver tag).
Hope this leads you to a solution. You might want to follow this sample code instead.
I am new to android programming . I am trying to develop simple gcm test application to register the app on gcm server. the code is
public class MainActivity extends ActionBarActivity implements OnClickListener {
Button btnRegId;
Context context;
EditText etRegId;
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
String regid;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
String SENDER_ID = "507707796382";
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
static final String TAG = "GCM";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
btnRegId = (Button) findViewById(R.id.btnGetRegId);
etRegId = (EditText) findViewById(R.id.etRegId);
if (checkPlayServices()) {
btnRegId.setOnClickListener(this);
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
Toast.makeText(getApplicationContext(), "No valid Google Play Services APK found.", Toast.LENGTH_LONG).show();
}
}
/**
* Gets the current registration ID for application on GCM service.
*
* 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);
}
}
#Override
public void onClick(View view) {
}
#Override
protected void onResume() {
super.onResume();
checkPlayServices();
}
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("GCM", "This device is not supported.");
finish();
}
return false;
}
return true;
}
/**
* Registers the application with GCM servers asynchronously.
*
* Stores the registration ID and app versionCode in the application's
* shared preferences.
*/
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// send the registration ID to server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to app.
// The request to server should be authenticated if app
// is using accounts.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the registration ID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
etRegId.setText(msg + "\n");
}
}.execute(null, null, null);
}
/**
* 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() {
// implementation .
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
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();
}
}
while executing this i get an error . My logcat window shows:
03-15 09:48:58.052 2686-2702/com.example.bhaskar.mygcm E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #1
Process: com.example.bhaskar.mygcm, PID: 2686
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.SecurityException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gms (has extras) } without permission com.google.android.c2dm.permission.RECEIVE
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1710)
at android.app.ContextImpl.startService(ContextImpl.java:1687)
at android.content.ContextWrapper.startService(ContextWrapper.java:515)
at com.google.android.gms.gcm.GoogleCloudMessaging.e(Unknown Source)
at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
at com.example.bhaskar.mygcm.MainActivity$1.doInBackground(MainActivity.java:158)
at com.example.bhaskar.mygcm.MainActivity$1.doInBackground(MainActivity.java:150)
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
On line no 158 I have ;
regid = gcm.register(SENDER_ID);
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bhaskar.mygcm" >
<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.C2D_RECEIVE"/>
<permission android:name="com.google.android.c2dm.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="com.google.android.c2dm.permission.C2D_MESSAGE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.C2D_SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="com.example.bhaskar.mygcm"/>
</intent-filter>
</receiver>
<service android:name=".GcmMessageHandler"/>
<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>
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version"/>
</application>
</manifest>
Can anyone help me figure out the bug.Any help would be appreciated.
have you added the
<uses-permission android:name=" com.google.android.c2dm.permission.RECEIVE " />
<uses-permission android:name="com.google.android.c2dm.permission.C2D_MESSAGE">
in your manifest? The logcat says otherwise.
I'm trying to make an app in Android Studio which requires you to download a file to the sdcard/Download/ folder on the users device. The problem is that when I enter the URL to download from and click Download, the app says "Unfortunately, * has stopped". I have quite a few files so here they are.
First, heres the Manifest...
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.NautGames.xecta.app" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.NautGames.xecta.app.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>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- Permission: Writing to SDCard -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
</manifest>
Heres the MainActivity class...
package com.NautGames.xecta.app;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
//Chat bot library
import org.alicebot.ab.Chat;
import org.alicebot.ab.Bot;
import android.view.View;
import android.widget.Button;
import android.os.Environment;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
TextView input;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//EditText mEdit = (EditText)findViewById(R.id.editText1);
public void buttonOnClick(View v)
{
input = (TextView) findViewById(R.id.editText1);
String dbPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/Ab";
Button button=(Button) v;
//Creating bot
String botname="xecta";
String path= dbPath;
Bot xecta = new Bot(botname, path);
Chat chatSession = new Chat(xecta);
String request = input.getText().toString();
String response = chatSession.multisentenceRespond(request);
((Button) v).setText(response);
}
public void buttonDownload(View v)
{
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
DownloaderThread (to download file)...
package com.NautGames.xecta.app;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import android.os.Environment;
import android.os.Message;
/**
* Downloads a file in a thread. Will send messages to the
* AndroidFileDownloader activity to update the progress bar.
*/
public class DownloaderThread extends Thread
{
// constants
private static final int DOWNLOAD_BUFFER_SIZE = 4096;
// instance variables
private AndroidFileDownloader parentActivity;
private String downloadUrl;
/**
//Instantiates a new DownloaderThread object.
// #param parentActivity Reference to AndroidFileDownloader activity.
// #param inUrl String representing the URL of the file to be downloaded.
*/
public DownloaderThread(AndroidFileDownloader inParentActivity, String inUrl)
{
downloadUrl = "";
if(inUrl != null)
{
downloadUrl = inUrl;
}
parentActivity = inParentActivity;
}
/**
* Connects to the URL of the file, begins the download, and notifies the
* AndroidFileDownloader activity of changes in state. Writes the file to
* the root of the SD card.
*/
#Override
public void run()
{
URL url;
URLConnection conn;
int fileSize, lastSlash;
String fileName;
BufferedInputStream inStream;
BufferedOutputStream outStream;
File outFile;
FileOutputStream fileStream;
Message msg;
// we're going to connect now
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_CONNECTING_STARTED,
0, 0, downloadUrl);
parentActivity.activityHandler.sendMessage(msg);
try
{
url = new URL(downloadUrl);
conn = url.openConnection();
conn.setUseCaches(false);
fileSize = conn.getContentLength();
// get the filename
lastSlash = url.toString().lastIndexOf('/');
fileName = "file.bin";
if(lastSlash >=0)
{
fileName = url.toString().substring(lastSlash + 1);
}
if(fileName.equals(""))
{
fileName = "file.bin";
}
// notify download start
int fileSizeInKB = fileSize / 1024;
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_DOWNLOAD_STARTED,
fileSizeInKB, 0, fileName);
parentActivity.activityHandler.sendMessage(msg);
// start download
inStream = new BufferedInputStream(conn.getInputStream());
outFile = new File(Environment.getExternalStorageDirectory() + "/" + fileName);
fileStream = new FileOutputStream(outFile);
outStream = new BufferedOutputStream(fileStream, DOWNLOAD_BUFFER_SIZE);
byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
int bytesRead = 0, totalRead = 0;
while(!isInterrupted() && (bytesRead = inStream.read(data, 0, data.length)) >= 0)
{
outStream.write(data, 0, bytesRead);
// update progress bar
totalRead += bytesRead;
int totalReadInKB = totalRead / 1024;
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_UPDATE_PROGRESS_BAR,
totalReadInKB, 0);
parentActivity.activityHandler.sendMessage(msg);
}
outStream.close();
fileStream.close();
inStream.close();
if(isInterrupted())
{
// the download was canceled, so let's delete the partially downloaded file
outFile.delete();
}
else
{
// notify completion
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_DOWNLOAD_COMPLETE);
parentActivity.activityHandler.sendMessage(msg);
}
}
catch(MalformedURLException e)
{
String errMsg = parentActivity.getString(R.string.error_message_bad_url);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
catch(FileNotFoundException e)
{
String errMsg = parentActivity.getString(R.string.error_message_file_not_found);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
catch(Exception e)
{
String errMsg = parentActivity.getString(R.string.error_message_general);
msg = Message.obtain(parentActivity.activityHandler,
AndroidFileDownloader.MESSAGE_ENCOUNTERED_ERROR,
0, 0, errMsg);
parentActivity.activityHandler.sendMessage(msg);
}
}
}
And lastly, The AndroidFileDownload(also to download file)...
package com.NautGames.xecta.app;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class AndroidFileDownloader extends Activity implements OnClickListener
{
// Used to communicate state changes in the DownloaderThread
public static final int MESSAGE_DOWNLOAD_STARTED = 1000;
public static final int MESSAGE_DOWNLOAD_COMPLETE = 1001;
public static final int MESSAGE_UPDATE_PROGRESS_BAR = 1002;
public static final int MESSAGE_DOWNLOAD_CANCELED = 1003;
public static final int MESSAGE_CONNECTING_STARTED = 1004;
public static final int MESSAGE_ENCOUNTERED_ERROR = 1005;
// instance variables
private AndroidFileDownloader thisActivity;
private Thread downloaderThread;
private ProgressDialog progressDialog;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
thisActivity = this;
downloaderThread = null;
progressDialog = null;
setContentView(R.layout.activity_main);
Button button = (Button) this.findViewById(R.id.download_button);
button.setOnClickListener(this);
}
/** Called when the user clicks on something. */
#Override
public void onClick(View view)
{
EditText urlInputField = (EditText) this.findViewById(R.id.url_input);
String urlInput = urlInputField.getText().toString();
downloaderThread = new DownloaderThread(thisActivity, urlInput);
downloaderThread.start();
}
/**
* This is the Handler for this activity. It will receive messages from the
* DownloaderThread and make the necessary updates to the UI.
*/
public Handler activityHandler = new Handler()
{
public void handleMessage(Message msg)
{
switch(msg.what)
{
/*
* Handling MESSAGE_UPDATE_PROGRESS_BAR:
* 1. Get the current progress, as indicated in the arg1 field
* of the Message.
* 2. Update the progress bar.
*/
case MESSAGE_UPDATE_PROGRESS_BAR:
if(progressDialog != null)
{
int currentProgress = msg.arg1;
progressDialog.setProgress(currentProgress);
}
break;
/*
* Handling MESSAGE_CONNECTING_STARTED:
* 1. Get the URL of the file being downloaded. This is stored
* in the obj field of the Message.
* 2. Create an indeterminate progress bar.
* 3. Set the message that should be sent if user cancels.
* 4. Show the progress bar.
*/
case MESSAGE_CONNECTING_STARTED:
if(msg.obj != null && msg.obj instanceof String)
{
String url = (String) msg.obj;
// truncate the url
if(url.length() > 16)
{
String tUrl = url.substring(0, 15);
tUrl += "...";
url = tUrl;
}
String pdTitle = thisActivity.getString(R.string.progress_dialog_title_connecting);
String pdMsg = thisActivity.getString(R.string.progress_dialog_message_prefix_connecting);
pdMsg += " " + url;
dismissCurrentProgressDialog();
progressDialog = new ProgressDialog(thisActivity);
progressDialog.setTitle(pdTitle);
progressDialog.setMessage(pdMsg);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setIndeterminate(true);
// set the message to be sent when this dialog is canceled
Message newMsg = Message.obtain(this, MESSAGE_DOWNLOAD_CANCELED);
progressDialog.setCancelMessage(newMsg);
progressDialog.show();
}
break;
/*
* Handling MESSAGE_DOWNLOAD_STARTED:
* 1. Create a progress bar with specified max value and current
* value 0; assign it to progressDialog. The arg1 field will
* contain the max value.
* 2. Set the title and text for the progress bar. The obj
* field of the Message will contain a String that
* represents the name of the file being downloaded.
* 3. Set the message that should be sent if dialog is canceled.
* 4. Make the progress bar visible.
*/
case MESSAGE_DOWNLOAD_STARTED:
// obj will contain a String representing the file name
if(msg.obj != null && msg.obj instanceof String)
{
int maxValue = msg.arg1;
String fileName = (String) msg.obj;
String pdTitle = thisActivity.getString(R.string.progress_dialog_title_downloading);
String pdMsg = thisActivity.getString(R.string.progress_dialog_message_prefix_downloading);
pdMsg += " " + fileName;
dismissCurrentProgressDialog();
progressDialog = new ProgressDialog(thisActivity);
progressDialog.setTitle(pdTitle);
progressDialog.setMessage(pdMsg);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setProgress(0);
progressDialog.setMax(maxValue);
// set the message to be sent when this dialog is canceled
Message newMsg = Message.obtain(this, MESSAGE_DOWNLOAD_CANCELED);
progressDialog.setCancelMessage(newMsg);
progressDialog.setCancelable(true);
progressDialog.show();
}
break;
/*
* Handling MESSAGE_DOWNLOAD_COMPLETE:
* 1. Remove the progress bar from the screen.
* 2. Display Toast that says download is complete.
*/
case MESSAGE_DOWNLOAD_COMPLETE:
dismissCurrentProgressDialog();
displayMessage(getString(R.string.user_message_download_complete));
break;
/*
* Handling MESSAGE_DOWNLOAD_CANCELLED:
* 1. Interrupt the downloader thread.
* 2. Remove the progress bar from the screen.
* 3. Display Toast that says download is complete.
*/
case MESSAGE_DOWNLOAD_CANCELED:
if(downloaderThread != null)
{
downloaderThread.interrupt();
}
dismissCurrentProgressDialog();
displayMessage(getString(R.string.user_message_download_canceled));
break;
/*
* Handling MESSAGE_ENCOUNTERED_ERROR:
* 1. Check the obj field of the message for the actual error
* message that will be displayed to the user.
* 2. Remove any progress bars from the screen.
* 3. Display a Toast with the error message.
*/
case MESSAGE_ENCOUNTERED_ERROR:
// obj will contain a string representing the error message
if(msg.obj != null && msg.obj instanceof String)
{
String errorMessage = (String) msg.obj;
dismissCurrentProgressDialog();
displayMessage(errorMessage);
}
break;
default:
// nothing to do here
break;
}
}
};
/**
* If there is a progress dialog, dismiss it and set progressDialog to
* null.
*/
public void dismissCurrentProgressDialog()
{
if(progressDialog != null)
{
progressDialog.hide();
progressDialog.dismiss();
progressDialog = null;
}
}
/**
* Displays a message to the user, in the form of a Toast.
* #param message Message to be displayed.
*/
public void displayMessage(String message)
{
if(message != null)
{
Toast.makeText(thisActivity, message, Toast.LENGTH_SHORT).show();
}
}
}
And heres the Log:
Process: com.NautGames.xecta.app, PID: 3032
java.lang.IllegalStateException: Could not find a method onClick(View) in the activity class com.NautGames.xecta.app.MainActivity for onClick handler on view class android.widget.Button with id 'download_button'
at android.view.View$1.onClick(View.java:3810)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.om.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NoSuchMethodException: onClick [class android.view.View]
at java.lang.Class.getConstructorOrMethod(Class.java:472)
at java.lang.Class.getMethod(Class.java:857)
at android.view.View$1.onClick(View.java:3803)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Could anyone tell me what the problem is?
Thanks in advance.
Since you said it's in AndroidFileDownload, I'd suggest you to do as follows:
#Override
public void onClick(View view)
// you need to get the id of the clicked view:
if(view.getId() == R.id.download_button) {
EditText urlInputField = (EditText) thisActivity.findViewById(R.id.url_input);
String urlInput = urlInputField.getText().toString();
downloaderThread = new DownloaderThread(thisActivity, urlInput);
downloaderThread.start();
}
}
This is the cause of: NoSuchMethodException: onClick [class android.view.View]. It means that your onClick method cannot find your button (id) where you did set an onClickListener method.
And then, in the future, I think the problem will be that AndroidFileDownload is not declared in your Manifest file. You should add it as follows:
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.NautGames.xecta.app.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>
// add every activity in your app like this below
<activity
android:name="com.NautGames.xecta.app.AndroidFileDownloader"
android:label="#string/app_name" />
</application>
What I try to do
Hello Guys, I'm trying to create some In-App-Products for my App, its for donation use. Because im giving away free musik of my friend - he raps. Whatever, I created 5 In-App Products on my developer-account:
donate_small
donate_midsmall
donate_medium
donate_large
donate_xlarge
This are the reference key's I generated there. They are saved and published. Now if written a In-App-Service over this tutorial: In-App Easy Tutorial. The Code seems to work perfect, there are no errors and when i compile the demo-code it works. But when I try it I get allways this error:
12-06 14:23:49.400: E/BillingService(4719): BillingHelper not fully instantiated
Question
So what do I need to change the the user can choose the diffrent in-app-products? Do I need to declare them somewhere?? Also if you have a great tutorial for me, where this stuff is all well describen and fully working, please tell me.
This are the Classes I use for my In-app-service:
de.stepforward
ChannelActivity
SplashActivity
de.stepforward.billing
AppMainTest.class -> This is my Activity
BillingHelper.class
BillingReceiver.class
BillingSecurity.class
BillingService.class
C.class
de.stepforward.billing.util
Base64.class
Base64DecoderException.class
For the moment I'll provide you the Code of my BillingHelper and Activity, if you need more Code just tell me.
Code
AppMainTest.class
package de.stepforward.billing;
import de.stepforward.R;
import de.stepforward.R.id;
import de.stepforward.R.layout;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
public class AppMainTest extends Activity implements OnClickListener{
private static final String TAG = "BillingService";
private Context mContext;
private ImageView purchaseableItem;
private Button purchaseButton;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("BillingService", "Starting");
setContentView(R.layout.donate);
mContext = this;
purchaseButton = (Button) findViewById(R.id.main_purchase_yes);
purchaseButton.setOnClickListener(this);
purchaseableItem = (ImageView) findViewById(R.id.main_purchase_item);
startService(new Intent(mContext, BillingService.class));
BillingHelper.setCompletedHandler(mTransactionHandler);
}
public Handler mTransactionHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
Log.i(TAG, "Transaction complete");
Log.i(TAG, "Transaction status: "+BillingHelper.latestPurchase.purchaseState);
Log.i(TAG, "Item purchased is: "+BillingHelper.latestPurchase.productId);
if(BillingHelper.latestPurchase.isPurchased()){
showItem();
}
};
};
public void onClick(View v) {
switch (v.getId()) {
case R.id.main_purchase_yes:
if(BillingHelper.isBillingSupported()){
BillingHelper.requestPurchase(mContext, "android.test.purchased");
// android.test.purchased or android.test.canceled or android.test.refunded or com.blundell.item.passport
} else {
Log.i(TAG,"Can't purchase on this device");
purchaseButton.setEnabled(false); // XXX press button before service started will disable when it shouldnt
}
break;
default:
// nada
Log.i(TAG,"default. ID: "+v.getId());
break;
}
}
private void showItem() {
purchaseableItem.setVisibility(View.VISIBLE);
}
#Override
protected void onPause() {
Log.i(TAG, "onPause())");
super.onPause();
}
#Override
protected void onDestroy() {
BillingHelper.stopService();
super.onDestroy();
}
}
BillingHelper.class
package de.stepforward.billing;
import java.util.ArrayList;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import com.android.vending.billing.IMarketBillingService;
import de.stepforward.billing.BillingSecurity.VerifiedPurchase;
import de.stepforward.billing.C.ResponseCode;
public class BillingHelper {
private static final String TAG = "BillingService";
private static IMarketBillingService mService;
private static Context mContext;
private static Handler mCompletedHandler;
protected static VerifiedPurchase latestPurchase;
protected static void instantiateHelper(Context context, IMarketBillingService service) {
mService = service;
mContext = context;
}
protected static void setCompletedHandler(Handler handler){
mCompletedHandler = handler;
}
protected static boolean isBillingSupported() {
if (amIDead()) {
return false;
}
Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED");
if (mService != null) {
try {
Bundle response = mService.sendBillingRequest(request);
ResponseCode code = ResponseCode.valueOf((Integer) response.get("RESPONSE_CODE"));
Log.i(TAG, "isBillingSupported response was: " + code.toString());
if (ResponseCode.RESULT_OK.equals(code)) {
return true;
} else {
return false;
}
} catch (RemoteException e) {
Log.e(TAG, "isBillingSupported response was: RemoteException", e);
return false;
}
} else {
Log.i(TAG, "isBillingSupported response was: BillingService.mService = null");
return false;
}
}
/**
* A REQUEST_PURCHASE request also triggers two asynchronous responses (broadcast intents).
* First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides error information about the request. (which I ignore)
* Next, if the request was successful, the Android Market application sends an IN_APP_NOTIFY broadcast intent.
* This message contains a notification ID, which you can use to retrieve the transaction details for the REQUEST_PURCHASE
* #param activityContext
* #param itemId
*/
protected static void requestPurchase(Context activityContext, String itemId){
if (amIDead()) {
return;
}
Log.i(TAG, "requestPurchase()");
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString("ITEM_ID", itemId);
try {
Bundle response = mService.sendBillingRequest(request);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
//The PURCHASE_INTENT key provides you with a PendingIntent, which you can use to launch the checkout UI
PendingIntent pendingIntent = (PendingIntent) response.get("PURCHASE_INTENT");
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "REQUEST_PURCHASE Sync Response code: "+responseCode.toString());
startBuyPageActivity(pendingIntent, new Intent(), activityContext);
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: "+isBillingSupported());
}
}
/**
* A GET_PURCHASE_INFORMATION request also triggers two asynchronous responses (broadcast intents).
* First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request. (which I ignore)
* Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent.
* This message contains detailed transaction information.
* The transaction information is contained in a signed JSON string (unencrypted).
* The message includes the signature so you can verify the integrity of the signed string
* #param notifyIds
*/
protected static void getPurchaseInformation(String[] notifyIds){
if (amIDead()) {
return;
}
Log.i(TAG, "getPurchaseInformation()");
Bundle request = makeRequestBundle("GET_PURCHASE_INFORMATION");
// The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate.
// The Android Market application returns this nonce with the PURCHASE_STATE_CHANGED broadcast intent so you can verify the integrity of the transaction information.
request.putLong("NONCE", BillingSecurity.generateNonce());
// The NOTIFY_IDS key contains an array of notification IDs, which you received in the IN_APP_NOTIFY broadcast intent.
request.putStringArray("NOTIFY_IDS", notifyIds);
try {
Bundle response = mService.sendBillingRequest(request);
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "GET_PURCHASE_INFORMATION Sync Response code: "+responseCode.toString());
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: "+isBillingSupported());
}
}
/**
* To acknowledge that you received transaction information you send a
* CONFIRM_NOTIFICATIONS request.
*
* A CONFIRM_NOTIFICATIONS request triggers a single asynchronous response—a RESPONSE_CODE broadcast intent.
* This broadcast intent provides status and error information about the request.
*
* Note: As a best practice, you should not send a CONFIRM_NOTIFICATIONS request for a purchased item until you have delivered the item to the user.
* This way, if your application crashes or something else prevents your application from delivering the product,
* your application will still receive an IN_APP_NOTIFY broadcast intent from Android Market indicating that you need to deliver the product
* #param notifyIds
*/
protected static void confirmTransaction(String[] notifyIds) {
if (amIDead()) {
return;
}
Log.i(TAG, "confirmTransaction()");
Bundle request = makeRequestBundle("CONFIRM_NOTIFICATIONS");
request.putStringArray("NOTIFY_IDS", notifyIds);
try {
Bundle response = mService.sendBillingRequest(request);
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "CONFIRM_NOTIFICATIONS Sync Response code: "+responseCode.toString());
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: " + isBillingSupported());
}
}
/**
*
* Can be used for when a user has reinstalled the app to give back prior purchases.
* if an item for sale's purchase type is "managed per user account" this means google will have a record ofthis transaction
*
* A RESTORE_TRANSACTIONS request also triggers two asynchronous responses (broadcast intents).
* First, the Android Market application sends a RESPONSE_CODE broadcast intent, which provides status and error information about the request.
* Next, if the request was successful, the Android Market application sends a PURCHASE_STATE_CHANGED broadcast intent.
* This message contains the detailed transaction information. The transaction information is contained in a signed JSON string (unencrypted).
* The message includes the signature so you can verify the integrity of the signed string
* #param nonce
*/
protected static void restoreTransactionInformation(Long nonce) {
if (amIDead()) {
return;
}
Log.i(TAG, "confirmTransaction()");
Bundle request = makeRequestBundle("RESTORE_TRANSACTIONS");
// The REQUEST_NONCE key contains a cryptographically secure nonce (number used once) that you must generate
request.putLong("NONCE", nonce);
try {
Bundle response = mService.sendBillingRequest(request);
//The REQUEST_ID key provides you with a unique request identifier for the request
Long requestIndentifier = (Long) response.get("REQUEST_ID");
Log.i(TAG, "current request is:" + requestIndentifier);
//The RESPONSE_CODE key provides you with the status of the request
Integer responseCodeIndex = (Integer) response.get("RESPONSE_CODE");
C.ResponseCode responseCode = C.ResponseCode.valueOf(responseCodeIndex);
Log.i(TAG, "RESTORE_TRANSACTIONS Sync Response code: "+responseCode.toString());
} catch (RemoteException e) {
Log.e(TAG, "Failed, internet error maybe", e);
Log.e(TAG, "Billing supported: " + isBillingSupported());
}
}
private static boolean amIDead() {
if (mService == null || mContext == null) {
Log.e(TAG, "BillingHelper not fully instantiated");
return true;
} else {
return false;
}
}
private static Bundle makeRequestBundle(String method) {
Bundle request = new Bundle();
request.putString("BILLING_REQUEST", method);
request.putInt("API_VERSION", 1);
request.putString("PACKAGE_NAME", mContext.getPackageName());
return request;
}
/**
*
*
* You must launch the pending intent from an activity context and not an application context
* You cannot use the singleTop launch mode to launch the pending intent
* #param pendingIntent
* #param intent
* #param context
*/
private static void startBuyPageActivity(PendingIntent pendingIntent, Intent intent, Context context){
//TODO add above 2.0 implementation with reflection, for now just using 1.6 implem
// This is on Android 1.6. The in-app checkout page activity will be on its
// own separate activity stack instead of on the activity stack of
// the application.
try {
pendingIntent.send(context, 0, intent);
} catch (CanceledException e){
Log.e(TAG, "startBuyPageActivity CanceledException");
}
}
protected static void verifyPurchase(String signedData, String signature) {
ArrayList<VerifiedPurchase> purchases = BillingSecurity.verifyPurchase(signedData, signature);
latestPurchase = purchases.get(0);
confirmTransaction(new String[]{latestPurchase.notificationId});
if(mCompletedHandler != null){
mCompletedHandler.sendEmptyMessage(0);
} else {
Log.e(TAG, "verifyPurchase error. Handler not instantiated. Have you called setCompletedHandler()?");
}
}
public static void stopService(){
mContext.stopService(new Intent(mContext, BillingService.class));
mService = null;
mContext = null;
mCompletedHandler = null;
Log.i(TAG, "Stopping Service");
}
}
Thx for your Help in Advance
Best Regards
safari
Appendix
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.stepforward"
android:versionCode="6"
android:versionName="1.3.2" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/StepForward.Theme"
android:debuggable="false"
>
<activity
android:label="#string/app_name"
android:name=".SplashActivity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ChannelActivity" ></activity>
<activity android:name=".billing.AppMainTest"></activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.vending.BILLING" />
<!-- In-App-Einkäufe für Donate -->
<service android:name=".BillingService" />
<receiver android:name=".BillingReceiver">
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
</intent-filter>
</receiver>
</manifest>
LogCat (some more information)
12-07 13:58:14.334: I/BillingService(20997): Starting
12-07 13:58:14.364: D/dalvikvm(20997): GC_EXTERNAL_ALLOC freed 15K, 58% free 2919K/6791K, external 414K/926K, paused 22ms
12-07 13:58:23.864: E/BillingService(20997): BillingHelper not fully instantiated
12-07 13:58:23.864: I/BillingService(20997): Can't purchase on this device
If you take a look at this method:
private static boolean amIDead() {
if (mService == null || mContext == null) {
Log.e(TAG, "BillingHelper not fully instantiated");
return true;
} else {
return false;
}
}
That log is printed when your Service is null or your context. Aaaand your service is null when:
protected static void instantiateHelper(Context context, IMarketBillingService service) {
mService = service;
mContext = context;
}
instantiateHelper is not called aaand
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "Market Billing Service Connected.");
mService = IMarketBillingService.Stub.asInterface(service);
BillingHelper.instantiateHelper(getBaseContext(), mService);
}
is called when your Service is connected, and I can see you attempt to connect to the service like this:
startService(new Intent(mContext, BillingService.class));
SO:
Have you declared the service in your manifest?
<application ...
<service android:name=".BillingService" />
...
</application>
EDIT
I was right :-) It's just your service tag is outside your application tag.
See here: https://stackoverflow.com/a/5439157/413127