So I have the following code and it is working fine, however I want to show the media controls in the users Notification area so they can play and stop the music as they please while the app is in the background.
I am wondering how one does this?
Code:
package com.radiomedia.a1liferadio;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private ImageView imagePlayPause;
private TextView textCurrentTime, textTotalDuration;
private SeekBar playerSeekBar;
private MediaPlayer mediaPlayer;
private Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imagePlayPause = findViewById(R.id.imagePlayPause);
textCurrentTime = findViewById(R.id.textCurrentTime);
textTotalDuration = findViewById(R.id.textTotalDuration);
playerSeekBar = findViewById(R.id.playerSeekBar);
mediaPlayer = new MediaPlayer();
playerSeekBar.setMax(100);
imagePlayPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(mediaPlayer.isPlaying())
{
handler.removeCallbacks(updater);
mediaPlayer.pause();
imagePlayPause.setImageResource(R.drawable.ic_play);
}else{
mediaPlayer.start();
imagePlayPause.setImageResource(R.drawable.ic_pause);
updateSeekBar();
}
}
});
prepareMediaPlayer();
}
private void prepareMediaPlayer() {
try {
mediaPlayer.setDataSource("http://stream.radiomedia.com.au:8003/stream"); //url of media
mediaPlayer.prepare();
textTotalDuration.setText(milliSecondsToTimer(mediaPlayer.getDuration()));
} catch (Exception exception){
Toast.makeText(this,exception.getMessage(), Toast.LENGTH_SHORT).show();
}
};
private Runnable updater = new Runnable() {
#Override
public void run() {
updateSeekBar();
long currentDuration = mediaPlayer.getCurrentPosition();
textCurrentTime.setText(milliSecondsToTimer(currentDuration));
}
};
private void updateSeekBar(){
if(mediaPlayer.isPlaying()) {
playerSeekBar.setProgress((int) (((float) mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration()) * 100));
handler.postDelayed(updater, 1000);
}
};
private String milliSecondsToTimer(long milliSeconds) {
String timerString = "";
String secondsString;
int hours = (int)(milliSeconds / (1000 * 60 * 60));
int minutes = (int)(milliSeconds % (1000 * 60 * 60)) / (1000 *60);
int seconds = (int)((milliSeconds % (1000 * 60 *60)) % (1000 * 60) / 1000);
if(hours > 0)
{
timerString = hours + ":";
}
if(seconds < 10)
{
secondsString = "0" + seconds;
}else{
secondsString = "" + seconds;
}
timerString = timerString + minutes + ":" + secondsString;
return timerString;
}
}
You should follow this old google mediaplayer sample (Use this commit if the master version crashes for you) if you want to keep using the MediaPlayer.
But If you don't mind using ExoPlayer, then you should instead follow this new one, since ExoPlayer can already take care of part of the notification stuff.
But so in these sample projects you can see that:
It's not as simple as just a notification, it also ties into people pressing on a play button on their headset etc.
You really need a service for this, to bind to the notification, to handle the action.MEDIA_BUTTON intents.
Minimal code for adding such a notification with a MediaSession and Service with the MediaPlayer (so not ExoPlayer) like you are using would look something like this:
Add the media compat libraries to your apps' build gradle:
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "androidx.media:media:1.1.0"
}
Then create a class for creating the Notification:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
import androidx.media.session.MediaButtonReceiver;
/**
* Keeps track of a notification and updates it automatically for a given MediaSession. This is
* required so that the music service don't get killed during playback.
*/
public class MediaNotificationManager {
public static final int NOTIFICATION_ID = 412;
private static final String TAG = MediaNotificationManager.class.getSimpleName();
private static final String CHANNEL_ID = "com.example.android.musicplayer.channel";
private static final int REQUEST_CODE = 501;
private final MediaSessionService mService;
private final NotificationCompat.Action mPlayAction;
private final NotificationCompat.Action mPauseAction;
private final NotificationManager mNotificationManager;
public MediaNotificationManager(MediaSessionService musicContext) {
mService = musicContext;
mNotificationManager =
(NotificationManager) mService.getSystemService(Service.NOTIFICATION_SERVICE);
mPlayAction =
new NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY));
mPauseAction =
new NotificationCompat.Action(
R.drawable.ic_pause,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE));
// Cancel all notifications to handle the case where the Service was killed and
// restarted by the system.
mNotificationManager.cancelAll();
}
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
}
public NotificationManager getNotificationManager() {
return mNotificationManager;
}
public Notification getNotification(MediaMetadataCompat metadata,
#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token) {
boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
MediaDescriptionCompat description = metadata.getDescription();
NotificationCompat.Builder builder =
buildNotification(state, token, isPlaying, description);
return builder.build();
}
private NotificationCompat.Builder buildNotification(#NonNull PlaybackStateCompat state,
MediaSessionCompat.Token token,
boolean isPlaying,
MediaDescriptionCompat description) {
// Create the (mandatory) notification channel when running on Android Oreo.
if (isAndroidOOrHigher()) {
createChannel();
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(mService, CHANNEL_ID);
builder.setStyle(
new MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0)
// For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP)))
.setColor(ContextCompat.getColor(mService, R.color.colorPrimary))
.setSmallIcon(R.drawable.ic_play)
// Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent())
// Title - Usually Song name.
.setContentTitle(description.getTitle())
// When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE));
builder.addAction(isPlaying ? mPauseAction : mPlayAction);
return builder;
}
// Does nothing on versions of Android earlier than O.
#RequiresApi(Build.VERSION_CODES.O)
private void createChannel() {
if (mNotificationManager.getNotificationChannel(CHANNEL_ID) == null) {
// The user-visible name of the channel.
CharSequence name = "MediaSession";
// The user-visible description of the channel.
String description = "MediaSession and MediaPlayer";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
// Configure the notification channel.
mChannel.setDescription(description);
mChannel.enableLights(true);
// Sets the notification light color for notifications posted to this
// channel, if the device supports this feature.
mChannel.setLightColor(Color.RED);
mChannel.enableVibration(true);
mChannel.setVibrationPattern(
new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
Log.d(TAG, "createChannel: New channel created");
} else {
Log.d(TAG, "createChannel: Existing channel reused");
}
}
private boolean isAndroidOOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
private PendingIntent createContentIntent() {
Intent openUI = new Intent(mService, MainActivity.class);
openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT);
}
}
Then Create a Service class that handles playback and the notification button presses:
package com.radiomedia.a1liferadio;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
public class MediaSessionService extends Service {
public MediaPlayer mediaPlayer;
public static final String TAG = "MediaSessionService";
public static final int NOTIFICATION_ID = 888;
private MediaNotificationManager mMediaNotificationManager;
private MediaSessionCompat mediaSession;
#Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mMediaNotificationManager = new MediaNotificationManager(this);
mediaSession = new MediaSessionCompat(this, "SOME_TAG");
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(new MediaSessionCompat.Callback() {
#Override
public void onPlay() {
mediaPlayer.start();
}
#Override
public void onPause() {
mediaPlayer.pause();
}
});
Notification notification =
mMediaNotificationManager.getNotification(
getMetadata(), getState(), mediaSession.getSessionToken());
startForeground(NOTIFICATION_ID, notification);
}
public MediaMetadataCompat getMetadata() {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist");
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title");
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer.getDuration()
);
return builder.build();
}
private PlaybackStateCompat getState() {
long actions = mediaPlayer.isPlaying() ? PlaybackStateCompat.ACTION_PAUSE : PlaybackStateCompat.ACTION_PLAY;
int state = mediaPlayer.isPlaying() ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;
final PlaybackStateCompat.Builder stateBuilder = new PlaybackStateCompat.Builder();
stateBuilder.setActions(actions);
stateBuilder.setState(state,
mediaPlayer.getCurrentPosition(),
1.0f,
SystemClock.elapsedRealtime());
return stateBuilder.build();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ("android.intent.action.MEDIA_BUTTON".equals(intent.getAction())) {
KeyEvent keyEvent = (KeyEvent) intent.getExtras().get("android.intent.extra.KEY_EVENT");
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer.pause();
} else {
mediaPlayer.start();
}
}
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Update your manifest for declaring the service and sending the action.MEDIA_BUTTON intent to it.
<service
android:name=".MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
This MediaButtonReceiver above is needed for pre-OREO devices, it will forward the event to the service on those platforms.
Then start the service from your main activity.
ContextCompat.startForegroundService(
MainActivity.this.getApplicationContext(),
new Intent(MainActivity.this.getApplicationContext(), MediaSessionService.class));
Things still to do here are making your main activity send actions to your service or the player in the service somehow, updating the notification and service state on a change, but you should be able to get something working with this code and figure out the rest from the google sample code.
There are two scenarios :
First
if you want to play song in background you should have to start a forground service. forground service is with notification .(pass the seekbar position, play pause state, playlist or video url to service).
After you creating custome notification with media controls whatever you want
you have to add a media player to service also pass your parameters like position , play pause state to service player that it play from your previous state of activity player.
on click of notification you have to do same parameters pass to activity .
Second
Start forground service with attaching a player and bind the activity to it, and handle all controls from notification and activity at the same time.
you can pass player controls to notfication in sense of froground service to start and play song in background .
you can use preference or tinydb for best performance in getting parameters to pass in first case.
Related
I'm trying to fire a notification when the battery level is less the %10.
here is my notification class located at MainActivity.java
public void notify(View view) {
String title = "Warning! low battery";
String text = "Please plug your mobile device to a charger";
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_qs_battery_saver)
.setContentTitle(title)
.setContentText(text)
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(notificationID++, notification);
}
I'm trying to fire it from the BatteryReceiver class:
package com.michal.ex2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.util.Log;
import android.view.View;
public class BatteryReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
int percent = (level * 100) / scale;
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean isPlugged;
isPlugged = plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
if ((percent == 10) && !isPlugged) {
Log.d("mylog", "low battery");
//notify();
synchronized(mActivity){
mActivity.notify();
}
}
}
}
First i tried calling calling notify from MainActivity, but the app crash with the error:
java.lang.IllegalMonitorStateException: object not locked by thread before notify()
I've searched for a solution and found that I need to lock the notify, so I tried calling it with synchronized():
synchronized(mActivity){
mActivity.notify();
but nothing happened. The battery receiver works alright(I'm getting the right log message) but notify() is not been called.
Maybe the function you'are calling is not the MainActivity.notify(View view) but the Object.notify(). Therefore nothing happens. Give an appropriate function argument.
mActivity.notify(null);
I'm fetching a JSON string from a server and write it to a file. I'm using java.util.Timer to run the task repeatedly every hour.
I want to run it exactly 00:00, 01:00, 02:00... 24:00(yes hourly).
Do I have to use Calendar + AlarmManager?
This is the code:
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import java.util.Timer;
import java.util.TimerTask;
public class LoggerServices extends Service {
LoggerTask loggerTask;
TimerTask timerTask;
Timer timer;
Handler handler;
public LoggerServices() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, "CHANNELID")
.setContentTitle("Example Service")
.setContentText("Population Logger")
.setSmallIcon(R.drawable.ic_mode_edit)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
handler = new Handler();
timer = new Timer();
timerTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
loggerTask = new LoggerTask();
loggerTask.execute();
}
});
}
};
timer.schedule(timerTask, 0, 3600000);
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
Log.d("DEBUG", "SERVICE DESTROYED");
if (loggerTask != null) {
loggerTask.cancel(true);
timerTask.cancel();
timer.cancel();
}
super.onDestroy();
}
}
The best option for you would probably be AlarmManager. You don't need to use any Calendar class or anything similar, because AlarmManager is already built for use with time in mind, because it needs to happen at an exact time.
One option is to use an alarm type. By doing this you are using the official time intervals provided by AlarmManager. You could also make your alarm go off every two hours by doing a loop of alarm managers, although that won't be that memory efficient and your android app could crash. Also, avoid using Services when trying to do exact-time works.
Because of the unlimited possibilities, it is impossible to create an implementation, so it would be better for you to decide for yourself.
Take a look at the official documentation: https://developer.android.com/reference/android/app/AlarmManager
Also, look at repeated alarms here if you want to do something related with alarms: https://developer.android.com/training/scheduling/alarms
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've got a normal java class (not an Activity) which implements an element of my app including a Notification with 2 actions: the first action fires a PendingIntent launching a brand-new activity while the other broadcasts an intent which will be caught in a Receiver class extending BroadcastReceiver and all goes well (for now is only playing a sound from RAW directory)
Since i need to update particular aspects of the object implemented in the first java class, I put the receiver in my class, extending BroadcastReceiver this way, but the test sound is not played at all. The code in the onReceive method is the same in both classes.
SingleInstance.java
package com.thepier.ingresstimer;
*various imports*
public class SingleInstance extends BroadcastReceiver {
/* Notification Misc */
private int ID;
private NotificationManager nm;
/* App context */
private Context cntx;
/* Contatore */
private int intCounter = 0;
/* Portal Data */
private int hackMax = 4; /* hackMax minimo */
private long coolTime = 300000; /* millisecondi = 5 minuti */
private long burnTime = 14400000; /* millisecondi = 4 ore */
/* Portal Cooldowns */
CountDownTimer cooldownTimer;
CountDownTimer burnoutTimer;
*other code*
public void showNotification () {
Intent intentPref = new Intent(cntx, PreferencesActivity.class);
PendingIntent pIntent1 = PendingIntent.getActivity(cntx, 0, intentPref, 0);
Intent intentHack = new Intent(cntx, SingleInstance.class);
//OR Intent intentHack = new Intent(cntx, HackReceiver.class);
intentHack.putExtra("ID", this.ID);
intentHack.putExtra("maxH", this.hackMax);
intentHack.putExtra("curH", this.intCounter);
PendingIntent pIntent2 = PendingIntent.getBroadcast(cntx, 0, intentHack, 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(cntx)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Cooldown terminato")
.setTicker("Cooldown terminato")
.addAction(R.drawable.ic_action_new, "Hack", pIntent2)
.addAction(R.drawable.ic_action_settings, "Impostazioni", pIntent1)
.setContentText("Hack: "+intCounter+"/"+hackMax);
nm.notify(ID, mBuilder.build());
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sfx_portal_hacking_alien);
mediaPlayer.start();
}
}
HackReceiver.java
package com.thepier.ingresstimer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
public class HackReceiver extends BroadcastReceiver {
public HackReceiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
// TODO Auto-generated method stub
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sfx_portal_hacking_alien);
mediaPlayer.start();
}
}
Problem solved: forgot the empty constructor needed by BroadcastReceiver
So basically im just new to Android, and im making a application which sends realtime notifications sended with php from server.
Now i want that the notification when clicked by the user opens a webpage in my webview.
Its a webbased app.
Im using 3 tabs to switch between 3 fragents.
Each fragment got a different webview.
Now this is mine GCMintentService.java:
package blah.blah;
import static blah.blah.CommonUtilities.SENDER_ID;
import static blah.blah.CommonUtilities.displayMessage;
import blah.blah.R;
import blah.blah.R.drawable;
import blah.blah.R.string;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = "GCMIntentService";
public GCMIntentService() {
super(SENDER_ID);
}
/**
* Method called on device registered
**/
#Override
protected void onRegistered(Context context, String registrationId) {
Log.i(TAG, "Toestel geregistreerd: regId = " + registrationId);
displayMessage(context, "Je toestel is geregistreerd");
Log.d("NAME", NotificationMain.name);
ServerUtilities.register(context, NotificationMain.name, NotificationMain.klas, registrationId);
}
/**
* Method called on device un registred
* */
#Override
protected void onUnregistered(Context context, String registrationId) {
Log.i(TAG, "Toestel nog niet geregistreerd!");
displayMessage(context, getString(R.string.gcm_unregistered));
ServerUtilities.unregister(context, registrationId);
}
/**
* Method called on Receiving a new message
* */
#Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Ontvangen bericht");
String message = intent.getExtras().getString("price");
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
/**
* Method called on receiving a deleted message
* */
#Override
protected void onDeletedMessages(Context context, int total) {
Log.i(TAG, "Received deleted messages notification");
String message = getString(R.string.gcm_deleted, total);
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
/**
* Method called on Error
* */
#Override
public void onError(Context context, String errorId) {
Log.i(TAG, "Received error: " + errorId);
displayMessage(context, getString(R.string.gcm_error, errorId));
}
#Override
protected boolean onRecoverableError(Context context, String errorId) {
// log message
Log.i(TAG, "Received recoverable error: " + errorId);
displayMessage(context, getString(R.string.gcm_recoverable_error,
errorId));
return super.onRecoverableError(context, errorId);
}
/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void generateNotification(Context context, String message) {
int icon = R.drawable.ic_launcher;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MyFragment.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// Play default notification sound
notification.defaults |= Notification.DEFAULT_SOUND;
//notification.sound = Uri.parse("android.resource://" + context.getPackageName() + "your_sound_file_name.mp3");
// Vibrate if vibrate is enabled
notification.defaults |= Notification.DEFAULT_VIBRATE;
notificationManager.notify(0, notification);
}
}
A main activity holds the 3 fragments with all differnt webviews.
I get the urls from a array called in FragmentAdapter.java where it extends FragmentAdapter.
Works perfect... here
is the fragmentAdapter:
package blah.blah;
import blah.blah.MyFragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class MyFragmentPagerAdapter extends FragmentPagerAdapter{
String[] toVisit={
"www.google.com",
"www.stackoverlow.com",
"www.lorumipsum.com",
};
final int PAGE_COUNT = 3;
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// Here is where all the magic of the adapter happens
// As you can see, this is really simple.
return MyFragment.newInstance(toVisit[position]);
}
#Override
public int getCount() {
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position) {
if(position == 0)
{
return "Klassen";
}
else if(position == 1)
{
return "Docenten";
}
else
{
return "Lokalen";
}
}
}
But how to load differnt urls depending on the retrieved info from the server? ,
The GCM push is sended with php with the usually code.
Should be something with intent.PushExtra("google.com, http://google.com") // for example
all doesn't work. I hope my question is clear.
All help is appriciated!
Just share all ur tips & thoughts:P.
Oh ye and my MyFragment.java if its usefull:
package blah.blah;
import blah.blah.R;
import blah.blah.R.id;
import blah.blah.R.layout;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
public class MyFragment extends Fragment{
WebView browser;
String url;
private Bundle webViewBundle;
Intent extras = getActivity().getIntent();
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view=inflater.inflate(
R.layout.myfragment_layout,
container,
false);
final ProgressBar spinner = (ProgressBar)view.findViewById(R.id.progress);
browser=(WebView)view.findViewById(R.id.webView1);
browser.getSettings().setJavaScriptEnabled(true);
browser.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
browser.loadUrl("file:///android_asset/geeninternet.html");
}
#SuppressWarnings("unused")
public void onProgressChanged(final WebView view, final int progress)
{
if(progress == 100)
spinner.setVisibility(View.GONE);
}
});
browser.loadUrl(url);
// Just load whatever URL this fragment is
// created with.
return view;
}
// This is the method the pager adapter will use
// to create a new fragment
public static Fragment newInstance(String url)
{
MyFragment f=new MyFragment();
f.url=url;
return f;
}
// Met browser;
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case R.id.menu_refresh:
browser.loadUrl(url);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* Sla webview op
*/
#Override
public void onPause()
{
super.onPause();
webViewBundle = new Bundle();
browser.saveState(webViewBundle);
}
/**
* Herstel staat van webview
*/
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
if (webViewBundle != null)
{
browser.restoreState(webViewBundle);
}
}
}
#Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Ontvangen bericht");
String message = intent.getExtras().getString("price");
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
In this code you take only one value from the notification payload - the value of the price key.
You didn't include the server code, so I don't know if you are sending a URL with the notification. If you do, you should get its value from the intent in the onMessage method and pass it to the code that open the web-view. If you don't, you should change your server code to include the URL in the notification payload.
EDIT :
In order to pass the URL to your fragment :
Get the URL from the extras of the intent in onMessage().
Pass it to generateNotification.
Add the URL as an extra to the intent that would start the activity when the notification is tapped : notificationIntent.putExtra ("url",url).
In the fragment, in the code where you initialize the web-view, get the URL from the extras of the intent that started the activity that contains the fragment (getActivity().getIntent().getExtras().getString("url").
EDIT2:
You should load the web-view in onActivityCreated(), not in onCreateView(). onCreateView() is called before the activity is created, so getActivity() will return null.
Don't use that code as is : String test = (getActivity().getIntent().getExtras().getString("url")); Make sure that getActivity() doesn't return null. I believe that when the getActivity() is not null, you can assume that getIntent() and getExtras() won't return null, but it's safer to check. getString("url") will return null if the activity wasn't opened from a notification. Therefore you should make sure that it has a value before loading the web-view.
There may be a problem with the notification flags :
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
It seems that the notification won't open a new activity if such an activity already exists (for example, if the app is already running). In that case, your fragment code for onCreateView() and onActivityCreated() won't be called and the URL won't be loaded. You should either change the flags so that a new activity will always be created, or you should put the logic that loads the web-view as a result of the notification somewhere else - probably in onResume().